+++ /dev/null
-table_name("processes")
-description("All running processes on the host system.")
-schema([
- Column("pid", BIGINT, "Process (or thread) ID", index=True),
- Column("name", TEXT, "The process path or shorthand argv[0]"),
- Column("path", TEXT, "Path to executed binary"),
- Column("cmdline", TEXT, "Complete argv"),
- Column("state", TEXT, "Process state"),
- Column("cwd", TEXT, "Process current working directory"),
- Column("root", TEXT, "Process virtual root directory"),
- Column("uid", BIGINT, "Unsigned user ID"),
- Column("gid", BIGINT, "Unsigned group ID"),
- Column("euid", BIGINT, "Unsigned effective user ID"),
- Column("egid", BIGINT, "Unsigned effective group ID"),
- Column("suid", BIGINT, "Unsigned saved user ID"),
- Column("sgid", BIGINT, "Unsigned saved group ID"),
- Column("on_disk", INTEGER,
- "The process path exists yes=1, no=0, unknown=-1"),
- Column("wired_size", BIGINT, "Bytes of unpagable memory used by process"),
- Column("resident_size", BIGINT, "Bytes of private memory used by process"),
- Column("total_size", BIGINT, "Total virtual memory size",
- aliases=["phys_footprint"]),
- Column("user_time", BIGINT, "CPU time in milliseconds spent in user space"),
- Column("system_time", BIGINT, "CPU time in milliseconds spent in kernel space"),
- Column("disk_bytes_read", BIGINT, "Bytes read from disk"),
- Column("disk_bytes_written", BIGINT, "Bytes written to disk"),
- Column("start_time", BIGINT, "Process start time in seconds since Epoch, in case of error -1"),
- Column("parent", BIGINT, "Process parent's PID"),
- Column("pgroup", BIGINT, "Process group"),
- Column("threads", INTEGER, "Number of threads used by process"),
- Column("nice", INTEGER, "Process nice level (-20 to 20, default 0)"),
-])
-extended_schema(WINDOWS, [
- Column("is_elevated_token", INTEGER, "Process uses elevated token yes=1, no=0"),
- Column("elapsed_time", BIGINT, "Elapsed time in seconds this process has been running."),
- Column("handle_count", BIGINT, "Total number of handles that the process has open. This number is the sum of the handles currently opened by each thread in the process."),
- Column("percent_processor_time", BIGINT, "Returns elapsed time that all of the threads of this process used the processor to execute instructions in 100 nanoseconds ticks."),
-])
-extended_schema(DARWIN, [
- Column("upid", BIGINT, "A 64bit pid that is never reused. Returns -1 if we couldn't gather them from the system."),
- Column("uppid", BIGINT, "The 64bit parent pid that is never reused. Returns -1 if we couldn't gather them from the system."),
- Column("cpu_type", INTEGER, "A 64bit pid that is never reused. Returns -1 if we couldn't gather them from the system."),
- Column("cpu_subtype", INTEGER, "The 64bit parent pid that is never reused. Returns -1 if we couldn't gather them from the system."),
-])
-attributes(cacheable=True, strongly_typed_rows=True)
-implementation("system/processes@genProcesses")
-examples([
- "select * from processes where pid = 1",
-])
+++ /dev/null
-table_name("file")
-description("Interactive filesystem attributes and metadata.")
-schema([
- Column("path", TEXT, "Absolute file path", required=True, index=True),
- Column("directory", TEXT, "Directory of file(s)", required=True),
- Column("filename", TEXT, "Name portion of file path"),
- Column("inode", BIGINT, "Filesystem inode number"),
- Column("uid", BIGINT, "Owning user ID"),
- Column("gid", BIGINT, "Owning group ID"),
- Column("mode", TEXT, "Permission bits"),
- Column("device", BIGINT, "Device ID (optional)"),
- Column("size", BIGINT, "Size of file in bytes"),
- Column("block_size", INTEGER, "Block size of filesystem"),
- Column("atime", BIGINT, "Last access time"),
- Column("mtime", BIGINT, "Last modification time"),
- Column("ctime", BIGINT, "Last status change time"),
- Column("btime", BIGINT, "(B)irth or (cr)eate time"),
- Column("hard_links", INTEGER, "Number of hard links"),
- Column("symlink", INTEGER, "1 if the path is a symlink, otherwise 0"),
- Column("type", TEXT, "File status"),
-])
-extended_schema(WINDOWS, [
- Column("attributes", TEXT, "File attrib string. See: https://ss64.com/nt/attrib.html"),
- Column("volume_serial", TEXT, "Volume serial number"),
- Column("file_id", TEXT, "file ID"),
- Column("product_version", TEXT, "File product version"),
-])
-attributes(utility=True)
-implementation("utility/file@genFile")
-examples([
- "select * from file where path = '/etc/passwd'",
- "select * from file where directory = '/etc/'",
- "select * from file where path LIKE '/etc/%'",
-])
## osquery v4.0.0
ADD_SUBDIRECTORY(core)
-ADD_SUBDIRECTORY(filesystem)
-ADD_SUBDIRECTORY(logger)
ADD_SUBDIRECTORY(registry)
ADD_SUBDIRECTORY(sql)
ADD_SUBDIRECTORY(tables)
#include "osquery/utils/info/platform_type.h"
#include <osquery/core.h>
#include <osquery/data_logger.h>
-#include <osquery/filesystem/filesystem.h>
#include <osquery/registry.h>
#include <osquery/utils/info/version.h>
#include <osquery/utils/system/time.h>
namespace {
extern "C" {
-static inline bool hasWorkerVariable() {
- return ::osquery::getEnvVar("OSQUERY_WORKER").is_initialized();
-}
-
volatile std::sig_atomic_t kHandledSignal{0};
void signalHandler(int num) {
}
void Initializer::initDaemon() const {
- if (isWorker() || !isDaemon()) {
- // The worker process (child) will not daemonize.
- return;
- }
-
- // Print the version to the OS system log.
- systemLog(binary_ + " started [version=" + kVersion + "]");
}
void Initializer::initShell() const {
}
}
-bool Initializer::isWorker() {
- return hasWorkerVariable();
-}
-
void Initializer::initActivePlugin(const std::string& type,
const std::string& name) const {
auto rs = RegistryFactory::get().setActive(type, name);
}
void Initializer::requestShutdown(int retcode, const std::string& system_log) {
- systemLog(system_log);
requestShutdown(retcode);
}
#include <boost/uuid/uuid_io.hpp>
#include <osquery/core.h>
-#include <osquery/filesystem/filesystem.h>
#include <osquery/logger.h>
#include <osquery/sql.h>
#include <osquery/system.h>
}
std::string generateHostUUID() {
- std::string hardware_uuid;
-#ifdef __APPLE__
- // Use the hardware UUID available on OSX to identify this machine
- uuid_t id;
- // wait at most 5 seconds for gethostuuid to return
- const timespec wait = {5, 0};
- if (gethostuuid(id, &wait) == 0) {
- char out[128] = {0};
- uuid_unparse(id, out);
- hardware_uuid = std::string(out);
- }
-#elif WIN32
- const WmiRequest wmiUUIDReq("Select UUID from Win32_ComputerSystemProduct");
- const std::vector<WmiResultItem>& wmiUUIDResults = wmiUUIDReq.results();
- if (wmiUUIDResults.size() != 0) {
- wmiUUIDResults[0].GetString("UUID", hardware_uuid);
- }
-#else
- readFile("/sys/class/dmi/id/product_uuid", hardware_uuid);
-#endif
-
- // We know at least Linux will append a newline.
- hardware_uuid.erase(
- std::remove(hardware_uuid.begin(), hardware_uuid.end(), '\n'),
- hardware_uuid.end());
- boost::algorithm::trim(hardware_uuid);
- if (!hardware_uuid.empty()) {
- // Construct a new string to remove trailing nulls.
- hardware_uuid = std::string(hardware_uuid.c_str());
- }
-
- // Check whether the UUID is valid. If not generate an ephemeral UUID.
- if (hardware_uuid.empty()) {
- VLOG(1) << "Failed to read system uuid, returning ephemeral uuid";
- return generateNewUUID();
- } else if (isPlaceholderHardwareUUID(hardware_uuid)) {
- VLOG(1) << "Hardware uuid '" << hardware_uuid
- << "' is a placeholder, returning ephemeral uuid";
- return generateNewUUID();
- } else {
- return hardware_uuid;
- }
+ return "Not supported";
}
Status getInstanceUUID(std::string& ident) {
+++ /dev/null
-# Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License
-
-ADD_OSQUERY_LIBRARY(osquery_filesystem filesystem.cpp
- mock_file_structure.cpp
- linux/mem.cpp
- linux/proc.cpp
- posix/fileops.cpp)
-
-FILE(GLOB OSQUERY_FILESYSTEM_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_FILESYSTEM_TESTS})
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#ifdef WIN32
-#include <iomanip>
-#include <map>
-#include <osquery/utils/system/system.h>
-#else
-#include <unistd.h>
-#endif
-
-#include <string>
-#include <vector>
-
-#include <boost/filesystem.hpp>
-#include <boost/noncopyable.hpp>
-#include <boost/optional.hpp>
-
-#include <osquery/utils/status/status.h>
-#include <osquery/utils/system/env.h>
-
-namespace osquery {
-
-#ifdef WIN32
-
-using mode_t = int;
-using ssize_t = SSIZE_T;
-using PlatformHandle = HANDLE;
-using PlatformTimeType = FILETIME;
-
-// Windows do not define these by default
-#define R_OK 4
-#define W_OK 2
-#define X_OK 1
-
-// Windows does not define these constants, and they are neater
-// than using raw octal for platformChmod, etc.
-#define S_IRUSR 0400
-#define S_IWUSR 0200
-#define S_IXUSR 0100
-#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
-
-#define S_IRGRP (S_IRUSR >> 3)
-#define S_IWGRP (S_IWUSR >> 3)
-#define S_IXGRP (S_IXUSR >> 3)
-#define S_IRWXG (S_IRWXU >> 3)
-
-#define S_IROTH (S_IRGRP >> 3)
-#define S_IWOTH (S_IWGRP >> 3)
-#define S_IXOTH (S_IXGRP >> 3)
-#define S_IRWXO (S_IRWXG >> 3)
-
-const std::map<std::int32_t, std::string> kDriveLetters{
- {0, "A:\\"}, {1, "B:\\"}, {2, "C:\\"}, {3, "D:\\"}, {4, "E:\\"},
- {5, "F:\\"}, {6, "G:\\"}, {7, "H:\\"}, {8, "I:\\"}, {9, "J:\\"},
- {10, "K:\\"}, {11, "L:\\"}, {12, "M:\\"}, {13, "N:\\"}, {14, "O:\\"},
- {15, "P:\\"}, {16, "Q:\\"}, {17, "R:\\"}, {18, "S:\\"}, {19, "T:\\"},
- {20, "U:\\"}, {21, "V:\\"}, {22, "W:\\"}, {23, "X:\\"}, {24, "Y:\\"},
- {25, "Z:\\"},
-};
-
-typedef struct win_stat {
- std::string path;
- std::string filename;
- int symlink;
- std::string file_id;
- LONGLONG inode;
- unsigned long uid;
- unsigned long gid;
- std::string mode;
- LONGLONG device;
- LONGLONG size;
- int block_size;
- LONGLONG atime;
- LONGLONG mtime;
- LONGLONG ctime;
- LONGLONG btime;
- int hard_links;
- std::string type;
- std::string attributes;
- std::string volume_serial;
- std::string product_version;
-
-} WINDOWS_STAT;
-
-#else
-
-using PlatformHandle = int;
-using PlatformTimeType = struct timeval;
-#endif
-
-typedef struct { PlatformTimeType times[2]; } PlatformTime;
-
-/// Constant for an invalid handle.
-const PlatformHandle kInvalidHandle = (PlatformHandle)-1;
-
-std::string lastErrorMessage(unsigned long);
-
-/**
- * @brief File access modes for PlatformFile.
- *
- * A file can be opened for many access modes with a variety of different
- * options on Windows and POSIX. To provide multi-platform support, we need to
- * provide an abstraction that can cover the supported platforms.
- */
-
-#define PF_READ 0x0001
-#define PF_WRITE 0x0002
-
-#define PF_OPTIONS_MASK 0x001c
-#define PF_GET_OPTIONS(x) ((x & PF_OPTIONS_MASK) >> 2)
-// Create new file only if it does not exist, or else fail.
-#define PF_CREATE_NEW (0 << 2)
-// If file exists truncate it, or else create new one.
-#define PF_CREATE_ALWAYS (1 << 2)
-// If file exists open it, or else fail.
-#define PF_OPEN_EXISTING (2 << 2)
-// If file exists open it, or else create new one.
-#define PF_OPEN_ALWAYS (3 << 2)
-#define PF_TRUNCATE (4 << 2)
-
-#define PF_NONBLOCK 0x0020
-#define PF_APPEND 0x0040
-
-/**
- * @brief Modes for seeking through a file.
- *
- * Provides a platform agnostic enumeration for file seek operations. These
- * are translated to the appropriate flags for the underlying platform.
- */
-enum SeekMode { PF_SEEK_BEGIN = 0, PF_SEEK_CURRENT, PF_SEEK_END };
-
-#ifdef WIN32
-/// Takes a Windows FILETIME object and returns seconds since epoch
-LONGLONG filetimeToUnixtime(const FILETIME& ft);
-
-LONGLONG longIntToUnixtime(LARGE_INTEGER& ft);
-
-std::string getFileAttribStr(unsigned long);
-
-Status platformStat(const boost::filesystem::path&, WINDOWS_STAT*);
-
-/**
- * @brief Stores information about the last Windows async request
- *
- * Currently, we have rudimentary support for non-blocking operations on
- * Windows. The implementation attempts to emulate POSIX non-blocking IO
- * semantics using the Windows asynchronous API. As such, there are currently
- * limitations. For example, opening a non-blocking file with read and write
- * privileges may produce some problems. If a write operation does not
- * immediately succeed, we cancel IO instead of waiting on it. As a result,
- * on-going async read operations will get canceled and data might get lost.
- *
- * Windows-only class that deals with simulating POSIX asynchronous IO semantics
- * using Windows API calls
- */
-struct AsyncEvent {
- AsyncEvent();
- ~AsyncEvent();
-
- OVERLAPPED overlapped_{0};
- std::unique_ptr<char[]> buffer_{nullptr};
- bool is_active_{false};
-};
-
-/*
- * @brief Converts a Windows short path to a full path
- *
- * This takes an 8.3 format path (i.e. C:\PROGRA~2\1PASSW~1\x64\AGILE1~1.DLL)
- * and converts to a full path
- *
- * @param shortPath the short path
- * @param rLongPath will be populated with the long path
- *
- * @return Success if successful, otherwise failure
- */
-Status windowsShortPathToLongPath(const std::string& shortPath,
- std::string& rLongPath);
-
-/*
- * @brief Get the product version associated with a file
- *
- * @param path: Full path to the file
- * @param rVersion: String representing the product version, e.g. "16.0.8201.0"
- *
- * @return Success if the version could be retrieved, otherwise failure
- */
-Status windowsGetFileVersion(const std::string& path, std::string& rVersion);
-#endif
-
-/**
- * @brief Platform-agnostic file object.
- *
- * PlatformFile is a multi-platform class that offers input/output capabilities
- * for files.
- */
-class PlatformFile : private boost::noncopyable {
- public:
- explicit PlatformFile(const boost::filesystem::path& path,
- int mode,
- int perms = -1);
- explicit PlatformFile(PlatformHandle handle) : handle_(handle) {}
-
- ~PlatformFile();
-
- /// Checks to see if the file object is "special file".
- bool isSpecialFile() const;
-
- /**
- * @brief Checks to see if there are any pending IO operations.
- *
- * This is mostly used after a read()/write() error in non-blocking mode to
- * determine the intention of the error. If read()/write() returns an error
- * and hasPendingIo() is true, this indicates that the read()/write()
- * operation didn't complete on time.
- */
- bool hasPendingIo() const {
- return has_pending_io_;
- }
-
- /// Checks to see if the handle backing the PlatformFile object is valid.
- bool isValid() const {
- return (handle_ != kInvalidHandle);
- }
-
- /// Returns the platform specific handle.
- PlatformHandle nativeHandle() const {
- return handle_;
- }
-
- /**
- * @brief Returns success if owner of the file is root.
- *
- * At the moment, we only determine that the owner of the current file is a
- * member of the Administrators group. We do not count files owned by
- * TrustedInstaller as owned by root.
- */
- Status isOwnerRoot() const;
-
- /// Returns success if the owner of the file is the current user.
- Status isOwnerCurrentUser() const;
-
- /// Determines whether the file has the executable bit set.
- Status isExecutable() const;
-
- /**
- * @brief Determines how immutable the file is to external modifications.
- *
- * Currently, this is only implemented on Windows. The Windows version of this
- * function ensures that writes are explicitly denied for the file AND the
- * file's parent directory.
- */
- Status hasSafePermissions() const;
-
- /// Return the modified, created, birth, updated, etc times.
- bool getFileTimes(PlatformTime& times);
-
- /// Change the file times.
- bool setFileTimes(const PlatformTime& times);
-
- /// Read a number of bytes into a buffer.
- ssize_t read(void* buf, size_t nbyte);
-
- /// Write a number of bytes from a buffer.
- ssize_t write(const void* buf, size_t nbyte);
-
- /// Use the platform-specific seek.
- off_t seek(off_t offset, SeekMode mode);
-
- /// Inspect the file size.
- size_t size() const;
-
- private:
- boost::filesystem::path fname_;
-
- /// The internal platform-specific open file handle.
- PlatformHandle handle_{kInvalidHandle};
-
- /// Is the file opened in a non-blocking read mode.
- bool is_nonblock_{false};
-
- /// Does the file have pending operations.
- bool has_pending_io_{false};
-
-#ifdef WIN32
- int cursor_{0};
-
- AsyncEvent last_read_;
-
- ssize_t getOverlappedResultForRead(void* buf, size_t requested_size);
-#endif
-};
-
-/**
- * @brief Returns the current user's home directory.
- *
- * This uses multiple methods to find the current user's home directory. It
- * attempts to use environment variables first and on failure, tries to obtain
- * the path using platform specific functions. Returns a boost::none on the
- * failure of both methods.
- */
-boost::optional<std::string> getHomeDirectory();
-
-/**
- * @brief Multi-platform implementation of chmod.
- * @note There are issues with the ACL being ordered "incorrectly". This
- * incorrect ordering does help with implementing the proper
- * behaviors
- *
- * This function approximates the functionality of the POSIX chmod function on
- * Windows. While there is the _chmod function on Windows, it does not support
- * the user, group, world permissions model. The Windows version of this
- * function will approximate it by using GetNamedSecurityInfoA to obtain the
- * file's owner and group. World is represented by the Everyone group on
- * Windows. Allowed permissions are represented by an access allowed access
- * control entry and unset permissions are represented by an explicit access
- * denied access control entry. However, the Windows preference for ACL ordering
- * creates some problems. For instance, if a user wishes to protect a file by
- * denying world access to a file, the normal standard for ACL ordering will end
- * up denying everyone, including the user, to the file (because of the deny
- * Everyone access control entry that is first in the ACL). To counter this, we
- * have to be more creative with the ACL order which presents some problems for
- * when attempting to modify permissions via File Explorer (complains of a
- * mis-ordered ACL and offers to rectify the problem).
- */
-bool platformChmod(const std::string& path, mode_t perms);
-
-/**
- * @brief Sets 'safe' permissions for the database backing osquery
- *
- * @note Safe DB perms are equivalent to a chmod 0700 for root on posix
- * so we emulate this by granting Full perms to SYSTEM and Administrators
- * only.
- */
-bool platformSetSafeDbPerms(const std::string& path);
-
-/**
- * @brief Multi-platform implementation of glob.
- * @note glob support is not 100% congruent with Linux glob. There are slight
- * differences in how GLOB_TILDE and GLOB_BRACE are implemented.
- *
- * This function approximates the functionality of the POSIX glob function on
- * Windows. It has naive support of GLOB_TILDE (doesn't support ~user syntax),
- * GLOB_MARK, and GLOB_BRACE (custom translation of glob expressions to regex).
- */
-std::vector<std::string> platformGlob(const std::string& find_path);
-
-/**
- * @brief Checks to see if the current user has the permissions to perform a
- * specified operation on a file.
- *
- * This abstracts the POSIX access function across Windows and POSIX. On
- * Windows, this calls the equivalent _access function.
- */
-int platformAccess(const std::string& path, mode_t mode);
-
-/**
- * @brief Checks to see if the provided directory is a temporary folder.
- * @note This just compares the temporary directory path against the given path
- * on Windows.
- */
-Status platformIsTmpDir(const boost::filesystem::path& dir);
-
-/// Determines the accessibility and existence of the file path.
-Status platformIsFileAccessible(const boost::filesystem::path& path);
-
-/// Determine if the FILE object points to a tty (console, serial port, etc).
-bool platformIsatty(FILE* f);
-
-/// Opens a file and returns boost::none on error
-boost::optional<FILE*> platformFopen(const std::string& filename,
- const std::string& mode);
-
-/**
- * @brief Checks for the existence of a named pipe or UNIX socket.
- *
- * This method is overloaded to perform two actions. If removal is requested
- * the success is determined based on the non-existence or successful removal
- * of the socket path. Otherwise the result is straightforward.
- *
- * The removal action is only used when extensions or the extension manager
- * is first starting.
- *
- * @param path The filesystem path to a UNIX socket or Windows named pipe.
- * @param remove_socket Attempt to remove the socket if it exists.
- *
- * @return Success if the socket exists and removal was not requested. False
- * if the socket exists and removal was requested (and the attempt to remove
- * had failed).
- */
-Status socketExists(const boost::filesystem::path& path,
- bool remove_socket = false);
-
-/**
- * @brief Returns the OS root system directory.
- *
- * Some applications store configuration and application data inside of the
- * Windows directory. This function retrieves the path to the current
- * configurations Windows location.
- *
- * On POSIX systems this returns "/".
- *
- * @return boost::filesystem::path containing the OS root location.
- */
-boost::filesystem::path getSystemRoot();
-
-/**
- * @brief Returns the successfully and fills d_stat if lstat was successful.
- *
- *
- * On Windows systems this does not touch the structure.
- *
- * @return osquery::Status
- */
-Status platformLstat(const std::string& path, struct stat& d_stat);
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <sstream>
-
-#include <fcntl.h>
-#include <sys/stat.h>
-
-#ifndef WIN32
-#include <glob.h>
-#include <pwd.h>
-#include <sys/time.h>
-#endif
-
-#include <boost/algorithm/string.hpp>
-#include <boost/filesystem/fstream.hpp>
-#include <boost/filesystem/operations.hpp>
-#include <boost/property_tree/json_parser.hpp>
-
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/sql.h>
-#include <osquery/system.h>
-#include <osquery/utils/system/system.h>
-
-#include <osquery/utils/json/json.h>
-
-namespace pt = boost::property_tree;
-namespace fs = boost::filesystem;
-namespace errc = boost::system::errc;
-
-namespace osquery {
-static const size_t kMaxRecursiveGlobs = 64;
-
-Status writeTextFile(const fs::path& path,
- const std::string& content,
- int permissions,
- int mode) {
- // Open the file with the request permissions.
- PlatformFile output_fd(path, mode, permissions);
- if (!output_fd.isValid()) {
- return Status(1, "Could not create file: " + path.string());
- }
-
- // If the file existed with different permissions before our open
- // they must be restricted.
- if (!platformChmod(path.string(), permissions)) {
- // Could not change the file to the requested permissions.
- return Status(1, "Failed to change permissions for file: " + path.string());
- }
-
- ssize_t bytes = output_fd.write(content.c_str(), content.size());
- if (static_cast<size_t>(bytes) != content.size()) {
- return Status(1, "Failed to write contents to file: " + path.string());
- }
-
- return Status::success();
-}
-
-struct OpenReadableFile : private boost::noncopyable {
- public:
- explicit OpenReadableFile(const fs::path& path, bool blocking = false) {
- int mode = PF_OPEN_EXISTING | PF_READ;
- if (!blocking) {
- mode |= PF_NONBLOCK;
- }
-
- // Open the file descriptor and allow caller to perform error checking.
- fd.reset(new PlatformFile(path, mode));
- }
-
- public:
- std::unique_ptr<PlatformFile> fd{nullptr};
-};
-
-Status readFile(const fs::path& path,
- size_t size,
- size_t block_size,
- bool dry_run,
- bool preserve_time,
- std::function<void(std::string& buffer, size_t size)> predicate,
- bool blocking) {
- OpenReadableFile handle(path, blocking);
- if (handle.fd == nullptr || !handle.fd->isValid()) {
- return Status(1, "Cannot open file for reading: " + path.string());
- }
-
- off_t file_size = static_cast<off_t>(handle.fd->size());
- if (handle.fd->isSpecialFile() && size > 0) {
- file_size = static_cast<off_t>(size);
- }
-
- // Apply the max byte-read based on file/link target ownership.
- auto read_max = static_cast<off_t>(2048);
- if (file_size > read_max) {
- if (!dry_run) {
- LOG(WARNING) << "Cannot read file that exceeds size limit: "
- << path.string();
- VLOG(1) << "Cannot read " << path.string()
- << " size exceeds limit: " << file_size << " > " << read_max;
- }
- return Status(1, "File exceeds read limits");
- }
-
- if (dry_run) {
- // The caller is only interested in performing file read checks.
- boost::system::error_code ec;
- try {
- return Status(0, fs::canonical(path, ec).string());
- } catch (const boost::filesystem::filesystem_error& err) {
- return Status(1, err.what());
- }
- }
-
- PlatformTime times;
- handle.fd->getFileTimes(times);
-
- off_t total_bytes = 0;
- if (file_size == 0 || block_size > 0) {
- // Reset block size to a sane minimum.
- block_size = (block_size < 4096) ? 4096 : block_size;
- ssize_t part_bytes = 0;
- bool overflow = false;
- do {
- std::string part(block_size, '\0');
- part_bytes = handle.fd->read(&part[0], block_size);
- if (part_bytes > 0) {
- total_bytes += static_cast<off_t>(part_bytes);
- if (total_bytes >= read_max) {
- return Status(1, "File exceeds read limits");
- }
- if (file_size > 0 && total_bytes > file_size) {
- overflow = true;
- part_bytes -= (total_bytes - file_size);
- }
- predicate(part, part_bytes);
- }
- } while (part_bytes > 0 && !overflow);
- } else {
- std::string content(file_size, '\0');
- do {
- auto part_bytes =
- handle.fd->read(&content[total_bytes], file_size - total_bytes);
- if (part_bytes > 0) {
- total_bytes += static_cast<off_t>(part_bytes);
- }
- } while (handle.fd->hasPendingIo());
- predicate(content, file_size);
- }
-
- return Status::success();
-}
-
-Status readFile(const fs::path& path,
- std::string& content,
- size_t size,
- bool dry_run,
- bool preserve_time,
- bool blocking) {
- return readFile(path,
- size,
- 4096,
- dry_run,
- preserve_time,
- ([&content](std::string& buffer, size_t _size) {
- if (buffer.size() == _size) {
- content += std::move(buffer);
- } else {
- content += buffer.substr(0, _size);
- }
- }),
- blocking);
-}
-
-Status readFile(const fs::path& path, bool blocking) {
- std::string blank;
- return readFile(path, blank, 0, true, false, blocking);
-}
-
-Status forensicReadFile(const fs::path& path,
- std::string& content,
- bool blocking) {
- return readFile(path, content, 0, false, true, blocking);
-}
-
-Status isWritable(const fs::path& path, bool effective) {
- auto path_exists = pathExists(path);
- if (!path_exists.ok()) {
- return path_exists;
- }
-
- if (effective) {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_WRITE);
- return Status(fd.isValid() ? 0 : 1);
- } else if (platformAccess(path.string(), W_OK) == 0) {
- return Status::success();
- }
-
- return Status(1, "Path is not writable: " + path.string());
-}
-
-Status isReadable(const fs::path& path, bool effective) {
- auto path_exists = pathExists(path);
- if (!path_exists.ok()) {
- return path_exists;
- }
-
- if (effective) {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- return Status(fd.isValid() ? 0 : 1);
- } else if (platformAccess(path.string(), R_OK) == 0) {
- return Status::success();
- }
-
- return Status(1, "Path is not readable: " + path.string());
-}
-
-Status pathExists(const fs::path& path) {
- boost::system::error_code ec;
- if (path.empty()) {
- return Status(1, "-1");
- }
-
- // A tri-state determination of presence
- if (!fs::exists(path, ec) || ec.value() != errc::success) {
- return Status(1, ec.message());
- }
- return Status::success();
-}
-
-Status movePath(const fs::path& from, const fs::path& to) {
- boost::system::error_code ec;
- if (from.empty() || to.empty()) {
- return Status(1, "Cannot copy empty paths");
- }
-
- fs::rename(from, to, ec);
- if (ec.value() != errc::success) {
- return Status(1, ec.message());
- }
- return Status(0);
-}
-
-Status removePath(const fs::path& path) {
- boost::system::error_code ec;
- auto removed_files = fs::remove_all(path, ec);
- if (ec.value() != errc::success) {
- return Status(1, ec.message());
- }
- return Status(0, std::to_string(removed_files));
-}
-
-static bool checkForLoops(std::set<int>& dsym_inos, std::string path) {
- if (path.empty() || path.back() != '/') {
- return false;
- }
-
- path.pop_back();
- struct stat d_stat;
- // On Windows systems (lstat not implemented) this immiedately returns
- if (!platformLstat(path, d_stat).ok()) {
- return false;
- }
-
- if ((d_stat.st_mode & 0170000) == 0) {
- return false;
- }
-
- if (dsym_inos.find(d_stat.st_ino) == dsym_inos.end()) {
- dsym_inos.insert(d_stat.st_ino);
- } else {
- LOG(WARNING) << "Symlink loop detected possibly involving: " << path;
- return true;
- }
- return false;
-}
-
-static void genGlobs(std::string path,
- std::vector<std::string>& results,
- GlobLimits limits) {
- // Use our helped escape/replace for wildcards.
- replaceGlobWildcards(path, limits);
- // inodes of directory symlinks for loop detection
- std::set<int> dsym_inos;
-
- // Generate a glob set and recurse for double star.
- for (size_t glob_index = 0; ++glob_index < kMaxRecursiveGlobs;) {
- auto glob_results = platformGlob(path);
-
- for (auto& result_path : glob_results) {
- results.push_back(result_path);
-
- if (checkForLoops(dsym_inos, result_path)) {
- glob_index = kMaxRecursiveGlobs;
- }
- }
-
- // The end state is a non-recursive ending or empty set of matches.
- size_t wild = path.rfind("**");
- // Allow a trailing slash after the double wild indicator.
- if (glob_results.size() == 0 || wild > path.size() ||
- wild + 3 < path.size()) {
- break;
- }
-
- path += "/**";
- }
-
- // Prune results based on settings/requested glob limitations.
- auto end = std::remove_if(
- results.begin(), results.end(), [limits](const std::string& found) {
- return !(((found[found.length() - 1] == '/' ||
- found[found.length() - 1] == '\\') &&
- limits & GLOB_FOLDERS) ||
- ((found[found.length() - 1] != '/' &&
- found[found.length() - 1] != '\\') &&
- limits & GLOB_FILES));
- });
- results.erase(end, results.end());
-}
-
-Status resolveFilePattern(const fs::path& fs_path,
- std::vector<std::string>& results) {
- return resolveFilePattern(fs_path, results, GLOB_ALL);
-}
-
-Status resolveFilePattern(const fs::path& fs_path,
- std::vector<std::string>& results,
- GlobLimits setting) {
- genGlobs(fs_path.string(), results, setting);
- return Status::success();
-}
-
-inline void replaceGlobWildcards(std::string& pattern, GlobLimits limits) {
- // Replace SQL-wildcard '%' with globbing wildcard '*'.
- if (pattern.find('%') != std::string::npos) {
- boost::replace_all(pattern, "%", "*");
- }
-
- // Relative paths are a bad idea, but we try to accommodate.
- if ((pattern.size() == 0 || ((pattern[0] != '/' && pattern[0] != '\\') &&
- (pattern.size() > 3 && pattern[1] != ':' &&
- pattern[2] != '\\' && pattern[2] != '/'))) &&
- pattern[0] != '~') {
- try {
- boost::system::error_code ec;
- pattern = (fs::current_path(ec) / pattern).make_preferred().string();
- } catch (const fs::filesystem_error& /* e */) {
- // There is a bug in versions of current_path that still throw.
- }
- }
-
- auto base =
- fs::path(pattern.substr(0, pattern.find('*'))).make_preferred().string();
-
- if (base.size() > 0) {
- boost::system::error_code ec;
- auto canonicalized = ((limits & GLOB_NO_CANON) == 0)
- ? fs::canonical(base, ec).make_preferred().string()
- : base;
-
- if (canonicalized.size() > 0 && canonicalized != base) {
- if (isDirectory(canonicalized)) {
- // Canonicalized directory paths will not include a trailing '/'.
- // However, if the wildcards are applied to files within a directory
- // then the missing '/' changes the wildcard meaning.
- canonicalized += '/';
- }
- // We are unable to canonicalize the meaning of post-wildcard limiters.
- pattern = fs::path(canonicalized + pattern.substr(base.size()))
- .make_preferred()
- .string();
- }
- }
-}
-
-inline Status listInAbsoluteDirectory(const fs::path& path,
- std::vector<std::string>& results,
- GlobLimits limits) {
- if (path.filename() == "*" && !pathExists(path.parent_path())) {
- return Status(1, "Directory not found: " + path.parent_path().string());
- }
-
- if (path.filename() == "*" && !isDirectory(path.parent_path())) {
- return Status(1, "Path not a directory: " + path.parent_path().string());
- }
-
- genGlobs(path.string(), results, limits);
- return Status::success();
-}
-
-Status listFilesInDirectory(const fs::path& path,
- std::vector<std::string>& results,
- bool recursive) {
- return listInAbsoluteDirectory(
- (path / ((recursive) ? "**" : "*")), results, GLOB_FILES);
-}
-
-Status listDirectoriesInDirectory(const fs::path& path,
- std::vector<std::string>& results,
- bool recursive) {
- return listInAbsoluteDirectory(
- (path / ((recursive) ? "**" : "*")), results, GLOB_FOLDERS);
-}
-
-Status isDirectory(const fs::path& path) {
- boost::system::error_code ec;
- if (fs::is_directory(path, ec)) {
- return Status::success();
- }
-
- // The success error code is returned for as a failure (undefined error)
- // We need to flip that into an error, a success would have falling through
- // in the above conditional.
- if (ec.value() == errc::success) {
- return Status(1, "Path is not a directory: " + path.string());
- }
- return Status(ec.value(), ec.message());
-}
-
-Status createDirectory(const boost::filesystem::path& dir_path,
- bool const recursive,
- bool const ignore_existence) {
- auto err = boost::system::error_code{};
- bool is_created = false;
- if (recursive) {
- is_created = boost::filesystem::create_directories(dir_path, err);
- } else {
- is_created = boost::filesystem::create_directory(dir_path, err);
- }
- if (is_created) {
- return Status::success();
- }
- if (ignore_existence && isDirectory(dir_path).ok()) {
- return Status::success();
- }
- auto msg = std::string{"Could not create directory \""};
- msg += dir_path.string();
- msg += '"';
- if (err) {
- msg += ": ";
- msg += err.message();
- }
- return Status::failure(msg);
-}
-
-std::set<fs::path> getHomeDirectories() {
- std::set<fs::path> results;
-
- auto users = SQL::selectAllFrom("users");
- for (const auto& user : users) {
- if (user.at("directory").size() > 0) {
- results.insert(user.at("directory"));
- }
- }
-
- return results;
-}
-
-bool safePermissions(const fs::path& dir,
- const fs::path& path,
- bool executable) {
- if (!platformIsFileAccessible(path).ok()) {
- // Path was not real, had too may links, or could not be accessed.
- return false;
- }
-
- Status result = platformIsTmpDir(dir);
- if (!result.ok() && result.getCode() < 0) {
- // An error has occurred in stat() on dir, most likely because the file path
- // does not exist
- return false;
- } else if (result.ok()) {
- // Do not load modules from /tmp-like directories.
- return false;
- }
-
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- if (!fd.isValid()) {
- return false;
- }
-
- result = isDirectory(path);
- if (!result.ok() && result.getCode() < 0) {
- // Something went wrong when determining the file's directoriness
- return false;
- } else if (result.ok()) {
- // Only load file-like nodes (not directories).
- return false;
- }
-
- if (fd.isOwnerRoot().ok() || fd.isOwnerCurrentUser().ok()) {
- result = fd.isExecutable();
-
- // Otherwise, require matching or root file ownership.
- if (executable && (result.getCode() > 0 || !fd.hasSafePermissions().ok())) {
- // Require executable, implies by the owner.
- return false;
- }
-
- return true;
- }
-
- // Do not load modules not owned by the user.
- return false;
-}
-
-const std::string& osqueryHomeDirectory() {
- static std::string homedir;
-
- if (homedir.size() == 0) {
- // Try to get the caller's home directory
- boost::system::error_code ec;
- auto userdir = getHomeDirectory();
- if (userdir.is_initialized() && isWritable(*userdir).ok()) {
- auto osquery_dir = (fs::path(*userdir) / ".osquery");
- if (isWritable(osquery_dir) ||
- boost::filesystem::create_directories(osquery_dir, ec)) {
- homedir = osquery_dir.make_preferred().string();
- return homedir;
- }
- }
-
- // Fail over to a temporary directory (used for the shell).
- auto temp =
- fs::temp_directory_path(ec) /
- (std::string("osquery-") + std::to_string((rand() % 10000) + 20000));
- boost::filesystem::create_directories(temp, ec);
- homedir = temp.make_preferred().string();
- }
-
- return homedir;
-}
-
-std::string lsperms(int mode) {
- static const char rwx[] = {'0', '1', '2', '3', '4', '5', '6', '7'};
- std::string bits;
-
- bits += rwx[(mode >> 9) & 7];
- bits += rwx[(mode >> 6) & 7];
- bits += rwx[(mode >> 3) & 7];
- bits += rwx[(mode >> 0) & 7];
- return bits;
-}
-
-Status parseJSON(const fs::path& path, pt::ptree& tree) {
- std::string json_data;
- if (!readFile(path, json_data).ok()) {
- return Status(1, "Could not read JSON from file");
- }
-
- return parseJSONContent(json_data, tree);
-}
-
-Status parseJSONContent(const std::string& content, pt::ptree& tree) {
- // Read the extensions data into a JSON blob, then property tree.
- try {
- std::stringstream json_stream;
- json_stream << content;
- pt::read_json(json_stream, tree);
- } catch (const pt::json_parser::json_parser_error& /* e */) {
- return Status(1, "Could not parse JSON from file");
- }
- return Status::success();
-}
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <osquery/filesystem/fileops.h>
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <boost/filesystem/path.hpp>
-#include <boost/property_tree/ptree.hpp>
-
-namespace osquery {
-
-class Status;
-
-/// Globbing directory traversal function recursive limit.
-enum GlobLimits : size_t {
- GLOB_FILES = 0x1,
- GLOB_FOLDERS = 0x2,
- GLOB_ALL = GLOB_FILES | GLOB_FOLDERS,
- GLOB_NO_CANON = 0x4,
-};
-
-inline GlobLimits operator|(GlobLimits a, GlobLimits b) {
- return static_cast<GlobLimits>(static_cast<size_t>(a) |
- static_cast<size_t>(b));
-}
-
-/// Globbing wildcard character.
-const std::string kSQLGlobWildcard{"%"};
-
-/// Globbing wildcard recursive character (double wildcard).
-const std::string kSQLGlobRecursive{kSQLGlobWildcard + kSQLGlobWildcard};
-
-/**
- * @brief Read a file from disk.
- *
- * @param path the path of the file that you would like to read.
- * @param size Number of bytes to read from file.
- * @param content a reference to a string which will be populated with the
- * contents of the path indicated by the path parameter.
- * @param dry_run do not actually read the file content.
- * @param preserve_time Attempt to preserve file mtime and atime.
- * @param blocking Request a blocking read.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status readFile(const boost::filesystem::path& path,
- std::string& content,
- size_t size = 0,
- bool dry_run = false,
- bool preserve_time = false,
- bool blocking = false);
-
-/// Read a file and preserve the atime and mtime.
-Status forensicReadFile(const boost::filesystem::path& path,
- std::string& content,
- bool blocking = false);
-
-/**
- * @brief Return the status of an attempted file read.
- *
- * @param path the path of the file that you would like to read.
- * @param blocking Request a blocking read.
- *
- * @return success iff the file would have been read. On success the status
- * message is the complete/absolute path.
- */
-Status readFile(const boost::filesystem::path& path, bool blocking = false);
-
-/// Internal representation for predicate-based chunk reading.
-Status readFile(const boost::filesystem::path& path,
- size_t size,
- size_t block_size,
- bool dry_run,
- bool preserve_time,
- std::function<void(std::string& buffer, size_t size)> predicate,
- bool blocking = false);
-
-/**
- * @brief Write text to disk.
- *
- * @param path the path of the file that you would like to write.
- * @param content the text that should be written exactly to disk.
- * @param permissions the filesystem permissions to request when opening.
- * @param mode to open file with
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status writeTextFile(const boost::filesystem::path& path,
- const std::string& content,
- int permissions = 0660,
- int mode = PF_OPEN_ALWAYS | PF_WRITE | PF_APPEND);
-
-/**
- * @brief Check if a path is writable.
- *
- * @param path The path of the file that you would like to write.
- * @param effective If you would like to check using effective UID
- *
- * @return A status returning if it's writable
- */
-Status isWritable(const boost::filesystem::path& path, bool effective = false);
-
-/**
- * @brief Check if a path is readable.
- *
- * @param path The path of the file that you would like to read.
- * @param effective If you would like to check using effective UID
- *
- * @return A status returning if it's readable
- */
-Status isReadable(const boost::filesystem::path& path, bool effective = false);
-
-/**
- * @brief A helper to check if a path exists on disk or not.
- *
- * @param path Target path.
- *
- * @return The code of the Status instance will be -1 if no input was supplied,
- * assuming the caller is not aware of how to check path-getter results.
- * The code will be 0 if the path does not exist on disk and 1 if the path
- * does exist on disk.
- */
-Status pathExists(const boost::filesystem::path& path);
-
-/**
- * @brief List all of the files in a specific directory.
- *
- * @param path the path which you would like to list.
- * @param results a non-const reference to a vector which will be populated
- * with the directory listing of the path param, assuming that all operations
- * completed successfully.
- * @param recursive should the listing descend recursively into the directory.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status listFilesInDirectory(const boost::filesystem::path& path,
- std::vector<std::string>& results,
- bool recursive = false);
-
-/**
- * @brief List all of the directories in a specific directory, non-recursively.
- *
- * @param path the path which you would like to list
- * @param results a non-const reference to a vector which will be populated
- * with the directory listing of the path param, assuming that all operations
- * completed successfully.
- * @param recursive should the listing descend recursively into the directory.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status listDirectoriesInDirectory(const boost::filesystem::path& path,
- std::vector<std::string>& results,
- bool recursive = false);
-
-/**
- * @brief Given a filesystem globbing patten, resolve all matching paths.
- *
- * @code{.cpp}
- * std::vector<std::string> results;
- * auto s = resolveFilePattern("/Users/marpaia/Downloads/%", results);
- * if (s.ok()) {
- * for (const auto& result : results) {
- * LOG(INFO) << result;
- * }
- * }
- * @endcode
- *
- * @param pattern filesystem globbing pattern.
- * @param results output vector of matching paths.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status resolveFilePattern(const boost::filesystem::path& pattern,
- std::vector<std::string>& results);
-
-/**
- * @brief Given a filesystem globbing patten, resolve all matching paths.
- *
- * See resolveFilePattern, but supply a limitation to request only directories
- * or files that match the path.
- *
- * @param pattern filesystem globbing pattern.
- * @param results output vector of matching paths.
- * @param setting a bit list of match types, e.g., files, folders.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status resolveFilePattern(const boost::filesystem::path& pattern,
- std::vector<std::string>& results,
- GlobLimits setting);
-
-/**
- * @brief Transform a path with SQL wildcards to globbing wildcard.
- *
- * SQL uses '%' as a wildcard matching token, and filesystem globbing uses '*'.
- * In osquery-internal methods the filesystem character is used. This helper
- * method will perform the correct preg/escape and replace.
- *
- * This has a side effect of canonicalizing paths up to the first wildcard.
- * For example: /tmp/% becomes /private/tmp/% on OS X systems. And /tmp/%.
- *
- * @param pattern the input and output filesystem glob pattern.
- * @param limits osquery::GlobLimits to apply (currently only recognizes
- * osquery::GLOB_NO_CANON)
- */
-void replaceGlobWildcards(std::string& pattern, GlobLimits limits = GLOB_ALL);
-
-/// Attempt to remove a directory path.
-Status removePath(const boost::filesystem::path& path);
-
-/// Move a file or directory to another path.
-Status movePath(const boost::filesystem::path& from,
- const boost::filesystem::path& to);
-
-/**
- * @brief Check if an input path is a directory.
- *
- * @param path input path, either a filename or directory.
- *
- * @return If the input path was a directory.
- */
-Status isDirectory(const boost::filesystem::path& path);
-
-/**
- * @brief Create the directory
- *
- * @param path to the intended directory
- * @param recursive - make parent directories as needed
- * @param ignore_existence - no error if directory already exists
- *
- * @return Status of operation
- */
-Status createDirectory(const boost::filesystem::path& path,
- bool recursive = false,
- bool ignore_existence = false);
-
-/**
- * @brief Return a vector of all home directories on the system.
- *
- * @return a vector of string paths containing all home directories.
- */
-std::set<boost::filesystem::path> getHomeDirectories();
-
-/**
- * @brief Check the permissions of a file and its directory.
- *
- * 'Safe' implies the directory is not a /tmp-like directory in that users
- * cannot control super-user-owner files. The file should be owned by the
- * process's UID or the file should be owned by root.
- *
- * @param dir the directory to check `/tmp` mode.
- * @param path a path to a file to check.
- * @param executable true if the file must also be executable.
- *
- * @return true if the file is 'safe' else false.
- */
-bool safePermissions(const boost::filesystem::path& dir,
- const boost::filesystem::path& path,
- bool executable = false);
-
-/**
- * @brief osquery may use local storage in a user-protected "home".
- *
- * Return a standard path to an "osquery" home directory. This path may store
- * a protected extensions socket, backing storage database, and debug logs.
- */
-const std::string& osqueryHomeDirectory();
-
-/// Return bit-mask-style permissions.
-std::string lsperms(int mode);
-
-/**
- * @brief Parse a JSON file on disk into a property tree.
- *
- * @param path the path of the JSON file.
- * @param tree output property tree.
- *
- * @return an instance of Status, indicating success or failure if malformed.
- */
-Status parseJSON(const boost::filesystem::path& path,
- boost::property_tree::ptree& tree);
-
-/**
- * @brief Parse JSON content into a property tree.
- *
- * @param content JSON string data.
- * @param tree output property tree.
- *
- * @return an instance of Status, indicating success or failure if malformed.
- */
-Status parseJSONContent(const std::string& content,
- boost::property_tree::ptree& tree);
-
-#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 an instance of Status, indicating success or failure.
- */
-Status procProcesses(std::set<std::string>& 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::map<std::string, std::string>& 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);
-
-/**
- * @brief Read bytes from Linux's raw memory.
- *
- * Most Linux kernels include a device node /dev/mem that allows privileged
- * users to map or seek/read pages of physical memory.
- * osquery discourages the use of physical memory reads for security and
- * performance reasons and must first try safer methods for data parsing
- * such as /sys and /proc.
- *
- * A platform user may disable physical memory reads:
- * --disable_memory=true
- * This flag/option will cause readRawMemory to forcefully fail.
- *
- * @param base The absolute memory address to read from. This does not need
- * to be page aligned, readRawMem will take care of alignment and only
- * return the requested start address and size.
- * @param length The length of the buffer with a max of 0x10000.
- * @param buffer The output buffer, caller is responsible for resources if
- * readRawMem returns success.
- * @return status The status of the read.
- */
-Status readRawMem(size_t base, size_t length, void** buffer);
-#endif
-
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <sys/mman.h>
-#include <sys/types.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-
-#define kLinuxMaxMemRead 0x10000
-
-const std::string kLinuxMemPath = "/dev/mem";
-
-Status readMem(int fd, size_t base, size_t length, uint8_t* buffer) {
- if (lseek(fd, base, SEEK_SET) == -1) {
- return Status(1, "Cannot seek to physical base");
- }
-
- // Read from raw memory until an unrecoverable read error or the all of the
- // requested bytes are read.
- size_t total_read = 0;
- ssize_t bytes_read = -1;
- while (total_read != length && bytes_read != 0) {
- bytes_read = read(fd, buffer + total_read, length - total_read);
- if (bytes_read == -1) {
- if (errno != EINTR) {
- return Status(1, "Cannot read requested length");
- }
- } else {
- total_read += bytes_read;
- }
- }
-
- // The read call finished without reading the requested number of bytes.
- if (total_read != length) {
- return Status(1, "Read incorrect number of bytes");
- }
-
- return Status::success();
-}
-
-Status readRawMem(size_t base, size_t length, void** buffer) {
- *buffer = 0;
-
- if (length > kLinuxMaxMemRead) {
- return Status(1, "Cowardly refusing to read a large number of bytes");
- }
-
- auto status = isReadable(kLinuxMemPath);
- if (!status.ok()) {
- // For non-su users *hopefully* raw memory is not readable.
- return status;
- }
-
- int fd = open(kLinuxMemPath.c_str(), O_RDONLY);
- if (fd < 0) {
- return Status(1, std::string("Cannot open ") + kLinuxMemPath);
- }
-
- if ((*buffer = malloc(length)) == nullptr) {
- close(fd);
- return Status(1, "Cannot allocate memory for read");
- }
-
-#ifdef _SC_PAGESIZE
- size_t offset = base % sysconf(_SC_PAGESIZE);
-#else
- // getpagesize() is more or less deprecated.
- size_t offset = base % getpagesize();
-#endif
-
- // Use memmap for maximum portability over read().
- auto map = mmap(0, offset + length, PROT_READ, MAP_SHARED, fd, base - offset);
- if (map == MAP_FAILED) {
- // Could fallback to a lseek/read.
- if (!readMem(fd, base, length, (uint8_t*)*buffer).ok()) {
- close(fd);
- free(*buffer);
- *buffer = nullptr;
- return Status(1, "Cannot memory map or seek/read memory");
- }
- } else {
- // Memory map succeeded, copy and unmap.
- memcpy(*buffer, (uint8_t*)map + offset, length);
- if (munmap(map, offset + length) == -1) {
- LOG(WARNING) << "Unable to unmap raw memory";
- }
- }
-
- close(fd);
- return Status::success();
-}
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <linux/limits.h>
-#include <unistd.h>
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/logger.h>
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/filesystem/linux/proc.h>
-#include <osquery/utils/conversions/split.h>
-
-
-namespace osquery {
-const std::vector<std::string> kUserNamespaceList = {
- "cgroup", "ipc", "mnt", "net", "pid", "user", "uts"};
-
-Status procGetNamespaceInode(ino_t& inode,
- const std::string& namespace_name,
- const std::string& process_namespace_root) {
- inode = 0;
-
- auto path = process_namespace_root + "/" + namespace_name;
-
- char link_destination[PATH_MAX] = {};
- auto link_dest_length = readlink(path.data(), link_destination, PATH_MAX - 1);
- if (link_dest_length < 0) {
- return Status(1, "Failed to retrieve the inode for namespace " + path);
- }
-
- // The link destination must be in the following form: namespace:[inode]
- if (std::strncmp(link_destination,
- namespace_name.data(),
- namespace_name.size()) != 0 ||
- std::strncmp(link_destination + namespace_name.size(), ":[", 2) != 0) {
- return Status(1, "Invalid descriptor for namespace " + path);
- }
-
- // Parse the inode part of the string; strtoull should return us a pointer
- // to the closing square bracket
- const char* inode_string_ptr = link_destination + namespace_name.size() + 2;
- char* square_bracket_ptr = nullptr;
-
- inode = static_cast<ino_t>(
- std::strtoull(inode_string_ptr, &square_bracket_ptr, 10));
- if (inode == 0 || square_bracket_ptr == nullptr ||
- *square_bracket_ptr != ']') {
- return Status(1, "Invalid inode value in descriptor for namespace " + path);
- }
-
- return Status::success();
-}
-
-Status procGetProcessNamespaces(const std::string& process_id,
- ProcessNamespaceList& namespace_list,
- std::vector<std::string> namespaces) {
- namespace_list.clear();
-
- if (namespaces.empty()) {
- namespaces = kUserNamespaceList;
- }
-
- auto process_namespace_root = kLinuxProcPath + "/" + process_id + "/ns";
-
- for (const auto& namespace_name : namespaces) {
- ino_t namespace_inode;
- auto status = procGetNamespaceInode(
- namespace_inode, namespace_name, process_namespace_root);
- if (!status.ok()) {
- continue;
- }
-
- namespace_list[namespace_name] = namespace_inode;
- }
-
- return Status::success();
-}
-
-std::string procDecodeAddressFromHex(const std::string& encoded_address,
- int family) {
- char addr_buffer[INET6_ADDRSTRLEN] = {0};
- if (family == AF_INET) {
- struct in_addr decoded;
- if (encoded_address.length() == 8) {
- sscanf(encoded_address.c_str(), "%X", &(decoded.s_addr));
- inet_ntop(AF_INET, &decoded, addr_buffer, INET_ADDRSTRLEN);
- }
-
- } else if (family == AF_INET6) {
- struct in6_addr decoded;
- if (encoded_address.length() == 32) {
- sscanf(encoded_address.c_str(),
- "%8x%8x%8x%8x",
- (unsigned int*)&(decoded.s6_addr[0]),
- (unsigned int*)&(decoded.s6_addr[4]),
- (unsigned int*)&(decoded.s6_addr[8]),
- (unsigned int*)&(decoded.s6_addr[12]));
- inet_ntop(AF_INET6, &decoded, addr_buffer, INET6_ADDRSTRLEN);
- }
- }
-
- return std::string(addr_buffer);
-}
-
-unsigned short procDecodePortFromHex(const std::string& encoded_port) {
- unsigned short decoded = 0;
- if (encoded_port.length() == 4) {
- sscanf(encoded_port.c_str(), "%hX", &decoded);
- }
- return decoded;
-}
-
-static Status procGetSocketListInet(int family,
- int protocol,
- ino_t net_ns,
- const std::string& path,
- const std::string& content,
- SocketInfoList& result) {
- // The system's socket information is tokenized by line.
- bool header = true;
- for (const auto& line : osquery::split(content, "\n")) {
- if (header) {
- if (line.find("sl") != 0 && line.find("sk") != 0) {
- return Status(1, std::string("Invalid file header for ") + path);
- }
- header = false;
- continue;
- }
-
- // The socket information is tokenized by spaces, each a field.
- auto fields = osquery::split(line, " ");
- if (fields.size() < 10) {
- VLOG(1) << "Invalid socket descriptor found: '" << line
- << "'. Skipping this entry";
- continue;
- }
-
- // Two of the fields are the local/remote address/port pairs.
- auto locals = osquery::split(fields[1], ":");
- auto remotes = osquery::split(fields[2], ":");
-
- if (locals.size() != 2 || remotes.size() != 2) {
- VLOG(1) << "Invalid socket descriptor found: '" << line
- << "'. Skipping this entry";
- continue;
- }
-
- SocketInfo socket_info = {};
- socket_info.socket = fields[9];
- socket_info.net_ns = net_ns;
- socket_info.family = family;
- socket_info.protocol = protocol;
- socket_info.local_address = procDecodeAddressFromHex(locals[0], family);
- socket_info.local_port = procDecodePortFromHex(locals[1]);
- socket_info.remote_address = procDecodeAddressFromHex(remotes[0], family);
- socket_info.remote_port = procDecodePortFromHex(remotes[1]);
-
- if (protocol == IPPROTO_TCP) {
- char* null_terminator_ptr = nullptr;
- auto integer_socket_state =
- std::strtoull(fields[3].data(), &null_terminator_ptr, 16);
- if (integer_socket_state == 0 ||
- integer_socket_state >= tcp_states.size() ||
- null_terminator_ptr == nullptr || *null_terminator_ptr != 0) {
- socket_info.state = "UNKNOWN";
- } else {
- socket_info.state = tcp_states[integer_socket_state];
- }
- }
-
- result.push_back(std::move(socket_info));
- }
-
- return Status(0);
-}
-
-static Status procGetSocketListUnix(ino_t net_ns,
- const std::string& path,
- const std::string& content,
- SocketInfoList& result) {
- // The system's socket information is tokenized by line.
- bool header = true;
- for (const auto& line : osquery::split(content, "\n")) {
- if (header) {
- if (line.find("Num") != 0) {
- return Status(1, std::string("Invalid file header for ") + path);
- }
- header = false;
- continue;
- }
-
- // The socket information is tokenized by spaces, each a field.
- auto fields = osquery::split(line, " ");
- if (fields.size() < 7) {
- VLOG(1) << "Invalid UNIX socket descriptor found: '" << line
- << "'. Skipping this entry";
- continue;
- }
-
- SocketInfo socket_info = {};
- socket_info.socket = fields[6];
- socket_info.net_ns = net_ns;
- socket_info.family = AF_UNIX;
- socket_info.protocol = std::atoll(fields[2].data());
- socket_info.unix_socket_path = (fields.size() >= 8) ? fields[7] : "";
-
- result.push_back(std::move(socket_info));
- }
-
- return Status(0);
-}
-
-Status procGetSocketList(int family,
- int protocol,
- ino_t net_ns,
- const std::string& pid,
- SocketInfoList& result) {
- std::string path = kLinuxProcPath + "/" + pid + "/net/";
-
- switch (family) {
- case AF_INET:
- if (kLinuxProtocolNames.count(protocol) == 0) {
- return Status(1,
- "Invalid family " + std::to_string(protocol) +
- " for AF_INET familiy");
- } else {
- path += kLinuxProtocolNames.at(protocol);
- }
- break;
-
- case AF_INET6:
- if (kLinuxProtocolNames.count(protocol) == 0) {
- return Status(1,
- "Invalid protocol " + std::to_string(protocol) +
- " for AF_INET6 familiy");
- } else {
- path += kLinuxProtocolNames.at(protocol) + "6";
- }
- break;
-
- case AF_UNIX:
- if (protocol != IPPROTO_IP) {
- return Status(1,
- "Invalid protocol " + std::to_string(protocol) +
- " for AF_UNIX familiy");
- } else {
- path += "unix";
- }
-
- break;
-
- default:
- return Status(1, "Invalid family " + std::to_string(family));
- }
-
- std::string content;
- if (!osquery::readFile(path, content).ok()) {
- return Status(1, "Could not open socket information from " + path);
- }
-
- Status status(0);
- switch (family) {
- case AF_INET:
- case AF_INET6:
- status =
- procGetSocketListInet(family, protocol, net_ns, path, content, result);
- break;
-
- case AF_UNIX:
- status = procGetSocketListUnix(net_ns, path, content, result);
- break;
- }
-
- return status;
-}
-
-Status procGetSocketInodeToProcessInfoMap(const std::string& pid,
- SocketInodeToProcessInfoMap& result) {
- auto callback = [](const std::string& _pid,
- const std::string& fd,
- const std::string& link,
- SocketInodeToProcessInfoMap& _result) -> bool {
- /* We only care about sockets. But there will be other descriptors. */
- if (link.find("socket:[") != 0) {
- return true;
- }
-
- std::string inode = link.substr(8, link.size() - 9);
- _result[inode] = {_pid, fd};
- return true;
- };
-
- return procEnumerateProcessDescriptors<decltype(result)>(
- pid, result, callback);
-}
-
-Status procProcesses(std::set<std::string>& processes) {
- auto callback = [](const std::string& pid,
- std::set<std::string>& _processes) -> bool {
- _processes.insert(pid);
- return true;
- };
-
- return procEnumerateProcesses<decltype(processes)>(processes, callback);
-}
-
-Status procDescriptors(const std::string& process,
- std::map<std::string, std::string>& descriptors) {
- auto callback = [](const std::string& pid,
- const std::string& fd,
- const std::string& link_name,
- std::map<std::string, std::string>& _descriptors) -> bool {
- _descriptors[fd] = link_name;
- return true;
- };
-
- return procEnumerateProcessDescriptors<decltype(descriptors)>(
- process, descriptors, callback);
-}
-
-Status procReadDescriptor(const std::string& process,
- const std::string& descriptor,
- std::string& result) {
- auto link = kLinuxProcPath + "/" + process + "/fd/" + descriptor;
-
- char result_path[PATH_MAX] = {0};
- auto size = readlink(link.c_str(), result_path, sizeof(result_path) - 1);
- if (size >= 0) {
- result = std::string(result_path);
- return Status(0);
- } else {
- return Status(1, "Could not read path");
- }
-}
-
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <unordered_map>
-
-#include <arpa/inet.h>
-#include <linux/limits.h>
-#include <unistd.h>
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/utils/conversions/tryto.h>
-
-namespace osquery {
-const std::string kLinuxProcPath = "/proc";
-
-struct SocketInfo final {
- std::string socket;
- ino_t net_ns;
-
- int family{0};
- int protocol{0};
-
- std::string local_address;
- std::uint16_t local_port{0U};
-
- std::string remote_address;
- std::uint16_t remote_port{0U};
-
- std::string unix_socket_path;
-
- std::string state;
-};
-typedef std::vector<SocketInfo> SocketInfoList;
-
-struct SocketProcessInfo final {
- std::string pid;
- std::string fd;
-};
-typedef std::map<std::string, SocketProcessInfo> SocketInodeToProcessInfoMap;
-
-// Linux proc protocol define to net stats file name.
-const std::map<int, std::string> kLinuxProtocolNames = {
- {IPPROTO_ICMP, "icmp"},
- {IPPROTO_TCP, "tcp"},
- {IPPROTO_UDP, "udp"},
- {IPPROTO_UDPLITE, "udplite"},
- {IPPROTO_RAW, "raw"},
-};
-
-const std::vector<std::string> tcp_states = {"UNKNOWN",
- "ESTABLISHED",
- "SYN_SENT",
- "SYN_RECV",
- "FIN_WAIT1",
- "FIN_WAIT2",
- "TIME_WAIT",
- "CLOSED",
- "CLOSE_WAIT",
- "LAST_ACK",
- "LISTEN",
- "CLOSING"};
-
-using ProcessNamespaceList = std::map<std::string, ino_t>;
-
-Status procGetProcessNamespaces(
- const std::string& process_id,
- ProcessNamespaceList& namespace_list,
- std::vector<std::string> namespaces = std::vector<std::string>());
-
-Status procReadDescriptor(const std::string& process,
- const std::string& descriptor,
- std::string& result);
-
-/// This function parses the inode value in the destination of a user namespace
-/// symlink; fail if the namespace name is now what we expect
-Status procGetNamespaceInode(ino_t& inode,
- const std::string& namespace_name,
- const std::string& process_namespace_root);
-
-std::string procDecodeAddressFromHex(const std::string& encoded_address,
- int family);
-
-unsigned short procDecodePortFromHex(const std::string& encoded_port);
-
-/**
- * @brief Construct a map of socket inode number to socket information collected
- * from /proc/<pid>/net for a certain family and protocol under a certain pid.
- *
- * The output parameter result is used as-is, i.e. it IS NOT cleared beforehand,
- * so values will either be added or replace existing ones without check.
- *
- * @param family The socket family. One of AF_INET, AF_INET6 or AF_UNIX.
- * @param protocol The socket protocol. For AF_INET and AF_INET6 one of the keys
- * @param pid Query data for this pid.
- * of kLinuxProtocolNames. For AF_UNIX only IPPROTO_IP is valid.
- * @param result The output parameter.
- */
-Status procGetSocketList(int family,
- int protocol,
- ino_t net_ns,
- const std::string& pid,
- SocketInfoList& result);
-
-/**
- * @brief Construct a map of socket inode number to process information for the
- * process that owns the socket by reading entries under /proc/<pid>/fd.
- *
- * The output parameter result is used as-is, i.e. it IS NOT cleared beforehand,
- * so values will either be added or replace existing ones without check.
- *
- * @param pid The process of interests
- * @param result The output parameter.
- */
-Status procGetSocketInodeToProcessInfoMap(const std::string& pid,
- SocketInodeToProcessInfoMap& result);
-
-/**
- * @brief Enumerate all pids in the system by listing pid numbers under /proc
- * and execute a callback for each one of them. The callback will receive the
- * pid and the user_data provided as argument.
- *
- * Notice there isn't any type of locking here so race conditions might occur,
- * e.g. a process is destroyed right before the callback being called.
- *
- * The loop will stop after the first callback failed, i.e. returned false.
- *
- * @param user_data User provided data to be passed to the callback
- * @param callback A pointer to the callback function
- */
-template <typename UserData>
-Status procEnumerateProcesses(UserData& user_data,
- bool (*callback)(const std::string&, UserData&)) {
- boost::filesystem::directory_iterator it(kLinuxProcPath), end;
-
- try {
- for (; it != end; ++it) {
- if (!boost::filesystem::is_directory(it->status())) {
- continue;
- }
-
- // See #792: std::regex is incomplete until GCC 4.9
- const auto& pid = it->path().leaf().string();
- if (std::atoll(pid.data()) <= 0) {
- continue;
- }
-
- bool ret = callback(pid, user_data);
- if (ret == false) {
- break;
- }
- }
- } catch (const boost::filesystem::filesystem_error& e) {
- VLOG(1) << "Exception iterating Linux processes: " << e.what();
- return Status(1, e.what());
- }
-
- return Status(0);
-}
-
-/**
- * @brief Enumerate all file descriptors of a certain process identified by its
- * pid by listing files under /proc/<pid>/fd and execute a callback for each one
- * of them. The callback will receive the pid the file descriptor and the real
- * path the file descriptor links to, and the user_data provided as argument.
- *
- * Notice there isn't any type of locking here so race conditions might occur,
- * e.g. a socket is closed right before the callback being called.
- *
- * The loop will stop after the first callback failed, i.e. returned false.
- *
- * @param pid The process id of interest
- * @param user_data User provided data to be passed to the callback
- * @param callback A pointer to the callback function
- */
-template <typename UserData>
-Status procEnumerateProcessDescriptors(const std::string& pid,
- UserData& user_data,
- bool (*callback)(const std::string& pid,
- const std::string& fd,
- const std::string& link,
- UserData& user_data)) {
- std::string descriptors_path = kLinuxProcPath + "/" + pid + "/fd";
-
- try {
- boost::filesystem::directory_iterator it(descriptors_path), end;
-
- for (; it != end; ++it) {
- auto fd = it->path().leaf().string();
-
- std::string link;
- Status status = procReadDescriptor(pid, fd, link);
- if (!status.ok()) {
- VLOG(1) << "Failed to read the link for file descriptor " << fd
- << " of pid " << pid << ". Data might be incomplete.";
- }
-
- bool ret = callback(pid, fd, link, user_data);
- if (ret == false) {
- break;
- }
- }
- } catch (boost::filesystem::filesystem_error& e) {
- VLOG(1) << "Exception iterating process file descriptors: " << e.what();
- return Status(1, e.what());
- }
-
- return Status(0);
-}
-
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <osquery/filesystem/filesystem.h>
-
-#include <boost/filesystem/path.hpp>
-
-namespace osquery {
-
-namespace fs = boost::filesystem;
-
-fs::path createMockFileStructure() {
- const auto root_dir =
- fs::temp_directory_path() /
- fs::unique_path("osquery.tests.%%%%.%%%%");
- fs::create_directories(root_dir / "toplevel/");
- fs::create_directories(root_dir / "toplevel/secondlevel1");
- fs::create_directories(root_dir / "toplevel/secondlevel2");
- fs::create_directories(root_dir / "toplevel/secondlevel3");
- fs::create_directories(root_dir / "toplevel/secondlevel3/thirdlevel1");
- fs::create_directories(root_dir / "deep11/deep2/deep3/");
- fs::create_directories(root_dir / "deep1/deep2/");
- writeTextFile(root_dir / "root.txt", "root");
- writeTextFile(root_dir / "door.txt", "toor", 0550);
- writeTextFile(root_dir / "roto.txt", "roto");
- writeTextFile(root_dir / "deep1/level1.txt", "l1");
- writeTextFile(root_dir / "deep11/not_bash", "l1");
- writeTextFile(root_dir / "deep1/deep2/level2.txt", "l2");
-
- writeTextFile(root_dir / "deep11/level1.txt", "l1");
- writeTextFile(root_dir / "deep11/deep2/level2.txt", "l2");
- writeTextFile(root_dir / "deep11/deep2/deep3/level3.txt", "l3");
-
-#ifdef WIN32
- writeTextFile(root_dir / "root2.txt", "l1");
-#else
- boost::system::error_code ec;
- fs::create_symlink(
- root_dir / "root.txt", root_dir / "root2.txt", ec);
-#endif
- return root_dir;
-}
-
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <boost/filesystem/path.hpp>
-
-namespace osquery {
-
-// generate a small directory structure for testing
-boost::filesystem::path createMockFileStructure();
-
-} // namespace
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/filesystem/fileops.h>
-
-#include <glob.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-
-#include <boost/filesystem.hpp>
-#include <boost/optional.hpp>
-
-namespace fs = boost::filesystem;
-namespace errc = boost::system::errc;
-
-namespace osquery {
-
-PlatformFile::PlatformFile(const fs::path& path, int mode, int perms)
- : fname_(path) {
- int oflag = 0;
- bool may_create = false;
- bool check_existence = false;
-
- if ((mode & PF_READ) == PF_READ && (mode & PF_WRITE) == PF_WRITE) {
- oflag = O_RDWR;
- } else if ((mode & PF_READ) == PF_READ) {
- oflag = O_RDONLY;
- } else if ((mode & PF_WRITE) == PF_WRITE) {
- oflag = O_WRONLY;
- }
-
- switch (PF_GET_OPTIONS(mode)) {
- case PF_GET_OPTIONS(PF_CREATE_ALWAYS):
- oflag |= O_CREAT | O_TRUNC;
- may_create = true;
- break;
- case PF_GET_OPTIONS(PF_CREATE_NEW):
- oflag |= O_CREAT | O_EXCL;
- may_create = true;
- break;
- case PF_GET_OPTIONS(PF_OPEN_EXISTING):
- check_existence = true;
- break;
- case PF_GET_OPTIONS(PF_OPEN_ALWAYS):
- oflag |= O_CREAT;
- may_create = true;
- break;
- case PF_GET_OPTIONS(PF_TRUNCATE):
- if (mode & PF_WRITE) {
- oflag |= O_TRUNC;
- }
-
- break;
- default:
- break;
- }
-
- if ((mode & PF_NONBLOCK) == PF_NONBLOCK) {
- oflag |= O_NONBLOCK;
- is_nonblock_ = true;
- }
-
- if ((mode & PF_APPEND) == PF_APPEND) {
- oflag |= O_APPEND;
- }
-
- if (perms == -1 && may_create) {
- perms = 0666;
- }
-
- boost::system::error_code ec;
- if (check_existence &&
- (!fs::exists(fname_, ec) || ec.value() != errc::success)) {
- handle_ = kInvalidHandle;
- } else {
- handle_ = ::open(fname_.c_str(), oflag, perms);
- }
-}
-
-PlatformFile::~PlatformFile() {
- if (handle_ != kInvalidHandle) {
- ::close(handle_);
- handle_ = kInvalidHandle;
- }
-}
-
-bool PlatformFile::isSpecialFile() const {
- return (size() == 0);
-}
-
-static uid_t getFileOwner(PlatformHandle handle) {
- struct stat file;
- if (::fstat(handle, &file) < 0) {
- return -1;
- }
- return file.st_uid;
-}
-
-Status PlatformFile::isOwnerRoot() const {
- if (!isValid()) {
- return Status(-1, "Invalid handle_");
- }
-
- uid_t owner_id = getFileOwner(handle_);
- if (owner_id == (uid_t)-1) {
- return Status(-1, "fstat error");
- }
-
- if (owner_id == 0) {
- return Status::success();
- }
- return Status(1, "Owner is not root");
-}
-
-Status PlatformFile::isOwnerCurrentUser() const {
- if (!isValid()) {
- return Status(-1, "Invalid handle_");
- }
-
- uid_t owner_id = getFileOwner(handle_);
- if (owner_id == (uid_t)-1) {
- return Status(-1, "fstat error");
- }
-
- if (owner_id == ::getuid()) {
- return Status::success();
- }
-
- return Status(1, "Owner is not current user");
-}
-
-Status PlatformFile::isExecutable() const {
- struct stat file_stat;
- if (::fstat(handle_, &file_stat) < 0) {
- return Status(-1, "fstat error");
- }
-
- if ((file_stat.st_mode & S_IXUSR) == S_IXUSR) {
- return Status::success();
- }
-
- return Status(1, "Not executable");
-}
-
-Status PlatformFile::hasSafePermissions() const {
- struct stat file;
- if (::fstat(handle_, &file) < 0) {
- return Status(-1, "fstat error");
- }
-
- // We allow user write for now, since our main threat is external
- // modification by other users
- if ((file.st_mode & S_IWOTH) == 0) {
- return Status::success();
- }
-
- return Status(1, "Writable");
-}
-
-bool PlatformFile::getFileTimes(PlatformTime& times) {
- if (!isValid()) {
- return false;
- }
-
- struct stat file;
- if (::fstat(handle_, &file) < 0) {
- return false;
- }
-
-#if defined(__linux__)
- TIMESPEC_TO_TIMEVAL(×.times[0], &file.st_atim);
- TIMESPEC_TO_TIMEVAL(×.times[1], &file.st_mtim);
-#else
- TIMESPEC_TO_TIMEVAL(×.times[0], &file.st_atimespec);
- TIMESPEC_TO_TIMEVAL(×.times[1], &file.st_mtimespec);
-#endif
-
- return true;
-}
-
-bool PlatformFile::setFileTimes(const PlatformTime& times) {
- if (!isValid()) {
- return false;
- }
-
- return (::futimes(handle_, times.times) == 0);
-}
-
-ssize_t PlatformFile::read(void* buf, size_t nbyte) {
- if (!isValid()) {
- return -1;
- }
-
- has_pending_io_ = false;
- auto ret = ::read(handle_, buf, nbyte);
- if (ret < 0 && errno == EAGAIN) {
- has_pending_io_ = true;
- } else if (ret > 0 && static_cast<size_t>(ret) < nbyte) {
- // This handles a (bug?) in Linux where special files are labeled as normal
- // for example: /sys nodes that must be read in pages.
- has_pending_io_ = true;
- }
-
- return ret;
-}
-
-ssize_t PlatformFile::write(const void* buf, size_t nbyte) {
- if (!isValid()) {
- return -1;
- }
-
- has_pending_io_ = false;
- auto ret = ::write(handle_, buf, nbyte);
- if (ret < 0 && errno == EAGAIN) {
- has_pending_io_ = true;
- }
- return ret;
-}
-
-off_t PlatformFile::seek(off_t offset, SeekMode mode) {
- if (!isValid()) {
- return -1;
- }
-
- int whence = 0;
- switch (mode) {
- case PF_SEEK_BEGIN:
- whence = SEEK_SET;
- break;
- case PF_SEEK_CURRENT:
- whence = SEEK_CUR;
- break;
- case PF_SEEK_END:
- whence = SEEK_END;
- break;
- default:
- break;
- }
- return ::lseek(handle_, offset, whence);
-}
-
-size_t PlatformFile::size() const {
- struct stat file;
- if (::fstat(handle_, &file) < 0) {
- // This is an error case, but the size is not signed.
- return 0;
- }
- return file.st_size;
-}
-
-boost::optional<std::string> getHomeDirectory() {
- // Try to get the caller's home directory using HOME and getpwuid.
- auto user = ::getpwuid(getuid());
- auto homedir = getEnvVar("HOME");
- if (homedir.is_initialized()) {
- // Fail over to the users home directory if HOME is not writable.
- if (isWritable(*homedir)) {
- return homedir;
- }
- }
-
- if (user != nullptr && user->pw_dir != nullptr) {
- return std::string(user->pw_dir);
- } else {
- return boost::none;
- }
-}
-
-bool platformSetSafeDbPerms(const std::string& path) {
- return platformChmod(path, S_IRWXU);
-}
-
-bool platformChmod(const std::string& path, mode_t perms) {
- return (::chmod(path.c_str(), perms) == 0);
-}
-
-std::vector<std::string> platformGlob(const std::string& find_path) {
- std::vector<std::string> results;
-
- glob_t data;
- ::glob(
- find_path.c_str(), GLOB_TILDE | GLOB_MARK | GLOB_BRACE, nullptr, &data);
- size_t count = data.gl_pathc;
-
- for (size_t index = 0; index < count; index++) {
- results.push_back(data.gl_pathv[index]);
- }
-
- ::globfree(&data);
- return results;
-}
-
-int platformAccess(const std::string& path, mode_t mode) {
- return ::access(path.c_str(), mode);
-}
-
-Status platformIsTmpDir(const fs::path& dir) {
- struct stat dir_stat;
- if (::stat(dir.c_str(), &dir_stat) < 0) {
- return Status(-1, "");
- }
-
- if (dir_stat.st_mode & (1 << 9)) {
- return Status::success();
- }
-
- return Status(1, "");
-}
-
-// Reduce this to be a lstat check for symlink stuff
-Status platformIsFileAccessible(const fs::path& path) {
- struct stat link_stat;
- if (::lstat(path.c_str(), &link_stat) < 0) {
- return Status(1, "File is not acccessible");
- }
- return Status::success();
-}
-
-bool platformIsatty(FILE* f) {
- return 0 != isatty(fileno(f));
-}
-
-boost::optional<FILE*> platformFopen(const std::string& filename,
- const std::string& mode) {
- auto fp = ::fopen(filename.c_str(), mode.c_str());
- if (fp == nullptr) {
- return boost::none;
- }
-
- return fp;
-}
-
-Status socketExists(const fs::path& path, bool remove_socket) {
- // This implies that the socket is writable.
- if (pathExists(path).ok()) {
- if (!isWritable(path).ok()) {
- return Status(1, "Cannot write extension socket: " + path.string());
- } else if (remove_socket && !removePath(path).ok()) {
- return Status(1, "Cannot remove extension socket: " + path.string());
- }
- } else {
- // The path does not exist.
- if (!pathExists(path.parent_path()).ok()) {
- return Status(1, "Extension socket directory missing: " + path.string());
- } else if (!isWritable(path.parent_path()).ok()) {
- return Status(1, "Cannot create extension socket: " + path.string());
- }
-
- // If we are not requesting to remove the socket then this is a failure.
- if (!remove_socket) {
- return Status(1, "Socket does not exist");
- }
- }
- return Status(0);
-}
-
-fs::path getSystemRoot() {
- return fs::path("/");
-}
-
-Status platformLstat(const std::string& path, struct stat& d_stat) {
- if (::lstat(path.c_str(), &d_stat) < 0) {
- return Status(1);
- }
- return Status(0);
-}
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <osquery/filesystem/fileops.h>
-
-#include <osquery/filesystem/mock_file_structure.h>
-
-#include <osquery/utils/info/platform_type.h>
-#include <osquery/utils/scope_guard.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/filesystem.hpp>
-
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-class FileOpsTests : public testing::Test {
- protected:
- fs::path fake_directory_;
-
- void SetUp() override {
- fake_directory_ = createMockFileStructure();
- }
-
- void TearDown() override {
- fs::remove_all(fake_directory_);
- }
-
- bool globResultsMatch(const std::vector<std::string>& results,
- const std::vector<fs::path>& expected) {
- // Sets cannot be the same if they are different sizes
- if (results.size() != expected.size()) {
- return false;
- }
- // Convert the data structure to a set for better searching
- std::set<std::string> results_set;
- for (const auto& res : results) {
- results_set.insert(res);
- }
-
- for (auto res : expected) {
- const auto loc = results_set.find(res.make_preferred().string());
- // Unable to find element (something is in expected but not results)
- if (loc == results_set.end()) {
- return false;
- }
- // Pair found so remove from results
- results_set.erase(loc);
- }
-
- // There are unremoved values so expected is a proper subset of results
- if (!results_set.empty()) {
- return false;
- }
- return true;
- }
-};
-
-class TempFile {
- public:
- TempFile() {
- do {
- path_ = generateTempPath();
- } while (fs::exists(path_));
- }
-
- ~TempFile() {
- if (fs::exists(path_)) {
- fs::remove(path_);
- }
- }
-
- const std::string& path() const {
- return path_;
- }
-
- private:
- static std::string generateTempPath() {
- return (fs::temp_directory_path() / fs::unique_path("osquery-%%%%-%%%%"))
- .make_preferred()
- .string();
- }
-
- private:
- std::string path_;
-};
-
-TEST_F(FileOpsTests, test_openFile) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- EXPECT_FALSE(fd.isValid());
- }
-
- {
- PlatformFile fd(path, PF_CREATE_NEW | PF_WRITE);
- EXPECT_TRUE(fd.isValid());
- }
-
- {
- PlatformFile fd(path, PF_CREATE_NEW | PF_READ);
- EXPECT_FALSE(fd.isValid());
- }
-
- fs::remove(path);
-
- {
- PlatformFile fd(path, PF_CREATE_ALWAYS | PF_READ);
- EXPECT_TRUE(fd.isValid());
- }
-
- {
- PlatformFile fd(path, PF_CREATE_ALWAYS | PF_READ);
- EXPECT_TRUE(fd.isValid());
- }
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- EXPECT_TRUE(fd.isValid());
- }
-}
-
-/*
- * This is a special function for testing file share operations on Windows. Our
- * PlatformFile as of now will only set FILE_SHARE_READ to play nicely with log
- * reading tools. However, we need to create one with FILE_SHARE_READ and
- * FILE_SHARE_WRITE for testing.
- */
-std::unique_ptr<PlatformFile> openRWSharedFile(const std::string& path,
- int mode) {
-#ifdef WIN32
- DWORD access_mask = -1;
- DWORD creation_disposition = -1;
-
- if (mode == (PF_OPEN_EXISTING | PF_READ)) {
- access_mask = PF_READ;
- creation_disposition = OPEN_EXISTING;
- } else if (mode == (PF_OPEN_ALWAYS | PF_WRITE)) {
- access_mask = PF_WRITE;
- creation_disposition = OPEN_ALWAYS;
- }
-
- HANDLE handle = ::CreateFileA(path.c_str(),
- access_mask,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr,
- creation_disposition,
- 0,
- nullptr);
- return std::unique_ptr<PlatformFile>(new PlatformFile(handle));
-#else
- return std::unique_ptr<PlatformFile>(new PlatformFile(path, mode));
-#endif
-}
-
-TEST_F(FileOpsTests, test_shareRead) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- const char* test1_data = "AAAABBBB";
- const ssize_t test1_size = ::strlen(test1_data);
-
- {
- PlatformFile fd(path, PF_CREATE_NEW | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(test1_size, fd.write(test1_data, test1_size));
- }
-
- {
- auto reader_fd = openRWSharedFile(path, PF_OPEN_EXISTING | PF_READ);
- ASSERT_TRUE(reader_fd->isValid());
-
- std::vector<char> buf;
- buf.assign(test1_size, '\0');
-
- EXPECT_EQ(test1_size, reader_fd->read(buf.data(), test1_size));
- EXPECT_EQ(static_cast<size_t>(test1_size), buf.size());
-
- for (ssize_t i = 0; i < test1_size; i++) {
- EXPECT_EQ(test1_data[i], buf[i]);
- }
-
- PlatformFile fd(path, PF_OPEN_ALWAYS | PF_WRITE | PF_APPEND);
- EXPECT_TRUE(fd.isValid());
- }
-}
-
-TEST_F(FileOpsTests, test_fileIo) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- const char* expected_read = "AAAABBBBCCCCDDDD";
- const ssize_t expected_read_len = ::strlen(expected_read);
- const ssize_t expected_write_len = ::strlen(expected_read);
- const size_t expected_buf_size = ::strlen(expected_read);
-
- {
- PlatformFile fd(path, PF_CREATE_NEW | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(expected_write_len, fd.write(expected_read, expected_read_len));
- }
-
- {
- std::vector<char> buf(expected_read_len);
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- ASSERT_TRUE(fd.isValid());
- ASSERT_FALSE(fd.isSpecialFile());
- EXPECT_EQ(expected_read_len, fd.read(buf.data(), expected_read_len));
- EXPECT_EQ(expected_buf_size, buf.size());
- for (ssize_t i = 0; i < expected_read_len; i++) {
- EXPECT_EQ(expected_read[i], buf[i]);
- }
- }
-}
-
-TEST_F(FileOpsTests, test_append) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- const char* test_data = "AAAABBBBCCCCDDDDD";
- const ssize_t test_size = ::strlen(test_data);
- const ssize_t test1_size = 7;
- const ssize_t test2_size = test_size - test1_size;
-
- {
- PlatformFile fd(path, PF_OPEN_ALWAYS | PF_WRITE | PF_APPEND);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(test1_size, fd.write(test_data, test1_size));
- }
-
- {
- PlatformFile fd(path, PF_OPEN_ALWAYS | PF_WRITE | PF_APPEND);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(test2_size, fd.write(&test_data[7], test2_size));
- }
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- ASSERT_TRUE(fd.isValid());
-
- std::vector<char> buf;
- buf.assign(test_size, '\0');
- EXPECT_EQ(test_size, fd.read(buf.data(), test_size));
- EXPECT_EQ(static_cast<size_t>(test_size), buf.size());
-
- for (ssize_t i = 0; i < test_size; i++) {
- EXPECT_EQ(test_data[i], buf[i]);
- }
- }
-}
-
-TEST_F(FileOpsTests, test_asyncIo) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- const char* expected = "AAAABBBBCCCCDDDDEEEEFFFFGGGG";
- const ssize_t expected_len = ::strlen(expected);
-
- {
- PlatformFile fd(path, PF_CREATE_NEW | PF_WRITE | PF_NONBLOCK);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(expected_len, fd.write(expected, expected_len));
- }
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ | PF_NONBLOCK);
- ASSERT_TRUE(fd.isValid());
- ASSERT_FALSE(fd.isSpecialFile());
-
- std::vector<char> buf(expected_len);
- EXPECT_EQ(expected_len, fd.read(buf.data(), expected_len));
- EXPECT_EQ(0, ::memcmp(expected, buf.data(), expected_len));
- }
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ | PF_NONBLOCK);
- ASSERT_TRUE(fd.isValid());
- ASSERT_FALSE(fd.isSpecialFile());
-
- std::vector<char> buf(expected_len);
- char* ptr = buf.data();
- ssize_t part_bytes = 0;
- int iterations = 0;
- do {
- part_bytes = fd.read(ptr, 4);
- if (part_bytes > 0) {
- ptr += part_bytes;
- iterations++;
- }
- } while (part_bytes > 0);
-
- EXPECT_EQ(7, iterations);
- EXPECT_EQ(0, ::memcmp(expected, buf.data(), expected_len));
- }
-}
-
-TEST_F(FileOpsTests, test_seekFile) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- const char* expected = "AABBBBAACCCAAAAADDDDAAAAAAAA";
- const ssize_t expected_len = ::strlen(expected);
- ssize_t expected_offs;
-
- {
- PlatformFile fd(path, PF_CREATE_ALWAYS | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(expected_len,
- fd.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAA", expected_len));
- }
-
- // Cast to the proper type, off_t
- expected_offs = expected_len - 12;
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
-
- EXPECT_EQ(expected_offs, fd.seek(-12, PF_SEEK_END));
- EXPECT_EQ(4, fd.write("DDDD", 4));
-
- EXPECT_EQ(2, fd.seek(2, PF_SEEK_BEGIN));
- EXPECT_EQ(4, fd.write("BBBB", 4));
-
- EXPECT_EQ(8, fd.seek(2, PF_SEEK_CURRENT));
- EXPECT_EQ(3, fd.write("CCC", 3));
- }
-
- {
- std::vector<char> buffer(expected_len);
-
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- ASSERT_TRUE(fd.isValid());
-
- EXPECT_EQ(expected_len, fd.read(buffer.data(), expected_len));
- EXPECT_EQ(0, ::memcmp(buffer.data(), expected, expected_len));
- }
-}
-
-TEST_F(FileOpsTests, test_large_read_write) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- const std::string expected(20000000, 'A');
- const ssize_t expected_len = expected.size();
- ASSERT_EQ(strnlen(expected.data(), 20000001), 20000000U);
-
- {
- PlatformFile fd(path, PF_CREATE_ALWAYS | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
- auto write_len = fd.write(expected.c_str(), expected_len);
- EXPECT_EQ(expected_len, write_len);
- }
-
- {
- std::vector<char> buffer(expected_len);
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- ASSERT_TRUE(fd.isValid());
- auto read_len = fd.read(buffer.data(), expected_len);
- EXPECT_EQ(expected_len, read_len);
- EXPECT_EQ(expected, std::string(buffer.data(), buffer.size()));
- }
-}
-
-TEST_F(FileOpsTests, test_chmod_no_exec) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- {
- PlatformFile fd(path, PF_CREATE_ALWAYS | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(4, fd.write("TEST", 4));
- }
-
- EXPECT_TRUE(platformChmod(path, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH));
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- ASSERT_TRUE(fd.isValid());
-
- auto status = fd.isExecutable();
- EXPECT_TRUE(!status.ok());
- EXPECT_EQ(1, status.getCode());
- }
-
- EXPECT_TRUE(platformChmod(
- path, S_IRUSR | S_IWUSR | S_IXUSR | S_IROTH | S_IWOTH | S_IXOTH));
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- ASSERT_TRUE(fd.isValid());
-
- EXPECT_TRUE(fd.isExecutable().ok());
- }
-}
-
-TEST_F(FileOpsTests, test_chmod_no_read) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- {
- PlatformFile fd(path, PF_CREATE_ALWAYS | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(4, fd.write("TEST", 4));
- }
-
- EXPECT_TRUE(platformChmod(path, S_IWUSR | S_IWOTH));
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- EXPECT_FALSE(fd.isValid());
- }
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_WRITE);
- EXPECT_TRUE(fd.isValid());
- }
-}
-
-TEST_F(FileOpsTests, test_chmod_no_write) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- {
- PlatformFile fd(path, PF_CREATE_ALWAYS | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(4, fd.write("TEST", 4));
- }
-
- EXPECT_TRUE(platformChmod(path, S_IRUSR | S_IROTH));
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_READ);
- EXPECT_TRUE(fd.isValid());
- }
-
- {
- PlatformFile fd(path, PF_OPEN_EXISTING | PF_WRITE);
- EXPECT_FALSE(fd.isValid());
- }
-}
-
-TEST_F(FileOpsTests, test_access) {
- const int all_access = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP |
- S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
-
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- {
- PlatformFile fd(path, PF_CREATE_ALWAYS | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(4, fd.write("TEST", 4));
- }
-
- EXPECT_TRUE(platformChmod(path, S_IRUSR | S_IWUSR | S_IXUSR));
-
- EXPECT_EQ(0, platformAccess(path, R_OK | W_OK | X_OK));
- EXPECT_EQ(0, platformAccess(path, R_OK | W_OK));
- EXPECT_EQ(0, platformAccess(path, R_OK | X_OK));
- EXPECT_EQ(0, platformAccess(path, W_OK | X_OK));
- EXPECT_EQ(0, platformAccess(path, R_OK));
- EXPECT_EQ(0, platformAccess(path, W_OK));
- EXPECT_EQ(0, platformAccess(path, X_OK));
-
- EXPECT_TRUE(platformChmod(path, S_IRUSR | S_IWUSR));
-
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK | X_OK));
- EXPECT_EQ(0, platformAccess(path, R_OK | W_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, W_OK | X_OK));
- EXPECT_EQ(0, platformAccess(path, R_OK));
- EXPECT_EQ(0, platformAccess(path, W_OK));
- EXPECT_EQ(-1, platformAccess(path, X_OK));
-
- EXPECT_TRUE(platformChmod(path, S_IRUSR | S_IXUSR));
-
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK));
- EXPECT_EQ(0, platformAccess(path, R_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, W_OK | X_OK));
- EXPECT_EQ(0, platformAccess(path, R_OK));
- EXPECT_EQ(-1, platformAccess(path, W_OK));
- EXPECT_EQ(0, platformAccess(path, X_OK));
-
- EXPECT_TRUE(platformChmod(path, S_IWUSR | S_IXUSR));
-
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | X_OK));
- EXPECT_EQ(0, platformAccess(path, W_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK));
- EXPECT_EQ(0, platformAccess(path, W_OK));
- EXPECT_EQ(0, platformAccess(path, X_OK));
-
- EXPECT_TRUE(platformChmod(path, S_IRUSR));
-
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, W_OK | X_OK));
- EXPECT_EQ(0, platformAccess(path, R_OK));
- EXPECT_EQ(-1, platformAccess(path, W_OK));
- EXPECT_EQ(-1, platformAccess(path, X_OK));
-
- EXPECT_TRUE(platformChmod(path, S_IWUSR));
-
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, W_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK));
- EXPECT_EQ(0, platformAccess(path, W_OK));
- EXPECT_EQ(-1, platformAccess(path, X_OK));
-
- EXPECT_TRUE(platformChmod(path, S_IXUSR));
-
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | W_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, W_OK | X_OK));
- EXPECT_EQ(-1, platformAccess(path, R_OK));
- EXPECT_EQ(-1, platformAccess(path, W_OK));
- EXPECT_EQ(0, platformAccess(path, X_OK));
-
- // Reset permissions
- EXPECT_TRUE(platformChmod(path, all_access));
-}
-
-TEST_F(FileOpsTests, test_safe_permissions) {
- const auto root_path = fs::temp_directory_path() /
- fs::unique_path("osquery.safe-perms-test.%%%%.%%%%");
- auto const root_path_manager =
- scope_guard::create([&root_path]() { fs::remove_all(root_path); });
-
- const auto temp_file = (root_path / "test").string();
- const auto root_dir = root_path.string();
-
- const int all_access = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP |
- S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
-
- fs::create_directories(root_dir);
-
- {
- PlatformFile fd(temp_file, PF_CREATE_ALWAYS | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
-
- EXPECT_TRUE(
- platformChmod(temp_file, S_IRUSR | S_IWGRP | S_IROTH | S_IWOTH));
- EXPECT_TRUE(platformChmod(root_dir, S_IRUSR | S_IRGRP | S_IROTH));
-
- auto status = fd.hasSafePermissions();
- EXPECT_FALSE(status.ok());
- EXPECT_EQ(1, status.getCode());
-
- if (isPlatform(PlatformType::TYPE_POSIX)) {
- // On POSIX, chmod on a file requires +x on the parent directory
- EXPECT_TRUE(platformChmod(root_dir, all_access));
- }
-
- EXPECT_TRUE(platformChmod(temp_file, S_IRUSR | S_IRGRP | S_IROTH));
- EXPECT_TRUE(platformChmod(root_dir,
- S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH));
-
- status = fd.hasSafePermissions();
-
- if (isPlatform(PlatformType::TYPE_WINDOWS)) {
- EXPECT_FALSE(status.ok());
- EXPECT_EQ(1, status.getCode());
- } else {
- // On POSIX, we only check to see if temp_file has S_IWOTH
- EXPECT_TRUE(status.ok());
- }
-
- if (isPlatform(PlatformType::TYPE_POSIX)) {
- // On POSIX, chmod on a file requires +x on the parent directory
- EXPECT_TRUE(platformChmod(root_dir, all_access));
- }
-
- EXPECT_TRUE(platformChmod(temp_file, S_IRUSR | S_IRGRP | S_IROTH));
- EXPECT_TRUE(platformChmod(root_dir, S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH));
-
- status = fd.hasSafePermissions();
-
- if (isPlatform(PlatformType::TYPE_WINDOWS)) {
- EXPECT_FALSE(status.ok());
- EXPECT_EQ(1, status.getCode());
- } else {
- // On POSIX, we only check to see if temp_file has S_IWOTH
- EXPECT_TRUE(status.ok());
- }
-
- if (isPlatform(PlatformType::TYPE_POSIX)) {
- // On POSIX, chmod on a file requires +x on the parent directory
- EXPECT_TRUE(platformChmod(root_dir, all_access));
- }
-
- EXPECT_TRUE(platformChmod(temp_file, 0));
- EXPECT_TRUE(platformChmod(root_dir, 0));
- EXPECT_TRUE(fd.hasSafePermissions().ok());
-
- if (isPlatform(PlatformType::TYPE_POSIX)) {
- // On POSIX, chmod on a file requires +x on the parent directory
- EXPECT_TRUE(platformChmod(root_dir, all_access));
- }
-
- EXPECT_TRUE(platformChmod(temp_file, S_IRUSR | S_IRGRP | S_IROTH));
- EXPECT_TRUE(platformChmod(root_dir, S_IRUSR | S_IRGRP | S_IROTH));
- EXPECT_TRUE(fd.hasSafePermissions().ok());
- }
-
- EXPECT_TRUE(platformChmod(root_dir, all_access));
- EXPECT_TRUE(platformChmod(temp_file, all_access));
-}
-
-TEST_F(FileOpsTests, test_safe_db_permissions) {
- const auto db_path =
- fs::temp_directory_path() /
- fs::unique_path("osquery.safe-db-perms-test.%%%%.%%%%.db");
- auto const db_path_manager =
- scope_guard::create([&db_path]() { fs::remove_all(db_path); });
-
- const auto sst_file = (db_path / "1234.sst").string();
- const auto db = db_path.string();
-
- fs::create_directories(db);
-
- // Ensure that 'safe' permissions get applied correctly
- {
- EXPECT_TRUE(platformSetSafeDbPerms(db));
-
- PlatformFile fd(sst_file, PF_CREATE_ALWAYS | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
-
- // The 'hasSafePermissions' function ensures no low priv writes can occur
- auto status = fd.hasSafePermissions();
-
- EXPECT_TRUE(fd.hasSafePermissions().ok());
- EXPECT_EQ(0, status.getCode());
- }
-
- // Ensure that we still have read and write access to the db
- {
- EXPECT_EQ(0, platformAccess(db, R_OK | W_OK));
- EXPECT_EQ(0, platformAccess(sst_file, R_OK | W_OK));
- }
-
- // Tear down our mock DB files
- const int all_access = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP |
- S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH;
- EXPECT_TRUE(platformChmod(db, all_access));
- EXPECT_TRUE(platformChmod(sst_file, all_access));
-}
-
-TEST_F(FileOpsTests, test_glob) {
- {
- std::vector<fs::path> expected{fake_directory_ / "door.txt",
- fake_directory_ / "root.txt",
- fake_directory_ / "root2.txt",
- fake_directory_ / "roto.txt"};
- auto result = platformGlob((fake_directory_ / "*.txt").string());
- EXPECT_TRUE(globResultsMatch(result, expected));
- }
-
- {
- std::vector<fs::path> expected{fake_directory_ / "deep1/",
- fake_directory_ / "deep11/",
- fake_directory_ / "door.txt",
- fake_directory_ / "root.txt",
- fake_directory_ / "root2.txt",
- fake_directory_ / "roto.txt",
- fake_directory_ / "toplevel/"};
- auto result = platformGlob((fake_directory_ / "*").string());
- EXPECT_TRUE(globResultsMatch(result, expected));
- }
-
- {
- std::vector<fs::path> expected{fake_directory_ / "deep1/deep2/",
- fake_directory_ / "deep1/level1.txt",
- fake_directory_ / "deep11/deep2/",
- fake_directory_ / "deep11/level1.txt",
- fake_directory_ / "deep11/not_bash",
- fake_directory_ / "toplevel/secondlevel1/",
- fake_directory_ / "toplevel/secondlevel2/",
- fake_directory_ / "toplevel/secondlevel3/"};
- auto result = platformGlob((fake_directory_ / "*" / "*").string());
- EXPECT_TRUE(globResultsMatch(result, expected));
- }
-
- {
- std::vector<fs::path> expected{
- fake_directory_ / "deep1/deep2/level2.txt",
- fake_directory_ / "deep11/deep2/deep3/",
- fake_directory_ / "deep11/deep2/level2.txt",
- fake_directory_ / "toplevel/secondlevel3/thirdlevel1/",
- };
- auto result = platformGlob((fake_directory_ / "*/*/*").string());
- EXPECT_TRUE(globResultsMatch(result, expected));
- }
-
- {
- std::vector<fs::path> expected{fake_directory_ / "deep11/deep2/deep3/",
- fake_directory_ / "deep11/deep2/level2.txt"};
- auto result = platformGlob((fake_directory_ / "*11/*/*").string());
- EXPECT_TRUE(globResultsMatch(result, expected));
- }
-
- {
- std::vector<fs::path> expected{fake_directory_ / "deep1/",
- fake_directory_ / "root.txt"};
- auto result = platformGlob((fake_directory_ / "{deep,root}{1,.txt}").string());
- EXPECT_TRUE(globResultsMatch(result, expected));
- }
-
- {
- std::vector<fs::path> expected{fake_directory_ / "deep1/deep2/level2.txt",
- fake_directory_ / "deep11/deep2/deep3/",
- fake_directory_ / "deep11/deep2/level2.txt"};
- auto result = platformGlob((fake_directory_ / "*/deep2/*").string());
- EXPECT_TRUE(globResultsMatch(result, expected));
- }
-
- {
- std::vector<fs::path> expected{fake_directory_ / "deep1/deep2/",
- fake_directory_ / "deep1/level1.txt",
- fake_directory_ / "deep11/deep2/",
- fake_directory_ / "deep11/level1.txt",
- fake_directory_ / "deep11/not_bash"};
- auto result =
- platformGlob((fake_directory_ / "*/{deep2,level1,not_bash}{,.txt}").string());
- EXPECT_TRUE(globResultsMatch(result, expected));
- }
-}
-
-TEST_F(FileOpsTests, test_zero_permissions_file) {
- TempFile tmp_file;
- std::string path = tmp_file.path();
-
- const std::string expected_str = "0_permissions";
- const ssize_t expected_len = expected_str.size();
-
- // Setup file for testing
- PlatformFile fd(path, PF_CREATE_NEW | PF_READ | PF_WRITE);
- ASSERT_TRUE(fd.isValid());
- EXPECT_EQ(expected_len, fd.write(expected_str.c_str(), expected_len));
- EXPECT_TRUE(platformChmod(path, 0));
-
- // Test file
- EXPECT_TRUE(!fd.isExecutable().ok());
-
- std::vector<char> buf(expected_len);
- EXPECT_EQ(0, fd.read(buf.data(), expected_len));
-
- auto modes = {R_OK, W_OK, X_OK};
- for (auto& mode : modes) {
- EXPECT_EQ(-1, platformAccess(path, mode));
- }
- EXPECT_EQ(boost::none, platformFopen(path, "r"));
-}
-} // namespace osquery
#include <boost/noncopyable.hpp>
#include <osquery/data_logger.h>
-#include <osquery/filesystem/filesystem.h>
#include <osquery/plugins/logger.h>
#include <osquery/registry_factory.h>
#include <osquery/system.h>
+++ /dev/null
-/*
- * 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 <exception>
-#include <mutex>
-
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-namespace pt = boost::property_tree;
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-const std::string kFilesystemLoggerFilename = "osqueryd.results.log";
-const std::string kFilesystemLoggerSnapshots = "osqueryd.snapshots.log";
-const std::string kFilesystemLoggerHealth = "osqueryd.health.log";
-
-std::mutex filesystemLoggerPluginMutex;
-
-class FilesystemLoggerPlugin : public LoggerPlugin {
- public:
- Status setUp();
- Status logString(const std::string& s);
- Status logStringToFile(const std::string& s, const std::string& filename);
- Status logSnapshot(const std::string& s);
- Status logHealth(const std::string& s);
- Status init(const std::string& name, const std::vector<StatusLogLine>& log);
- Status logStatus(const std::vector<StatusLogLine>& log);
-
- private:
- fs::path log_path_;
-};
-
-REGISTER(FilesystemLoggerPlugin, "logger", "filesystem");
-
-Status FilesystemLoggerPlugin::setUp() {
- return Status(0, "OK");
-}
-
-Status FilesystemLoggerPlugin::logString(const std::string& s) {
- return logStringToFile(s, kFilesystemLoggerFilename);
-}
-
-Status FilesystemLoggerPlugin::logStringToFile(const std::string& s,
- const std::string& filename) {
- std::lock_guard<std::mutex> lock(filesystemLoggerPluginMutex);
- try {
- // The results log may contain sensitive information if run as root.
- auto status = writeTextFile((log_path_ / filename).string(), s, 0640, true);
- if (!status.ok()) {
- return status;
- }
- } catch (const std::exception& e) {
- return Status(1, e.what());
- }
- return Status(0, "OK");
-}
-
-Status FilesystemLoggerPlugin::logStatus(
- const std::vector<StatusLogLine>& log) {
- for (const auto& item : log) {
- // Emit this intermediate log to the Glog filesystem logger.
- google::LogMessage(item.filename.c_str(),
- item.line,
- (google::LogSeverity)item.severity).stream()
- << item.message;
- }
-
- return Status(0, "OK");
-}
-
-Status FilesystemLoggerPlugin::logSnapshot(const std::string& s) {
- // Send the snapshot data to a separate filename.
- return logStringToFile(s, kFilesystemLoggerSnapshots);
-}
-
-Status FilesystemLoggerPlugin::logHealth(const std::string& s) {
- return logStringToFile(s, kFilesystemLoggerHealth);
-}
-
-Status FilesystemLoggerPlugin::init(const std::string& name,
- const std::vector<StatusLogLine>& log) {
- // Stop the internal Glog facilities.
- google::ShutdownGoogleLogging();
-
- // Restart the Glog facilities using the name `init` was provided.
- google::InitGoogleLogging(name.c_str());
-
- // We may violate Glog global object assumptions. So set names manually.
- auto basename = (log_path_ / name).string();
- google::SetLogDestination(google::INFO, (basename + ".INFO.").c_str());
- google::SetLogDestination(google::WARNING, (basename + ".WARNING.").c_str());
- google::SetLogDestination(google::ERROR, (basename + ".ERROR.").c_str());
-
- // Now funnel the intermediate status logs provided to `init`.
- logStatus(log);
-
- // The filesystem logger cheats and uses Glog to log to the filesystem so
- // we can return failure here and stop the custom log sink.
- return Status(1, "No status logger used for filesystem");
-}
-}
+++ /dev/null
-/*
- * 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 <syslog.h>
-
-#include <osquery/logger.h>
-#include <osquery/utils/conversions/split.h>
-
-namespace osquery {
-
-class SyslogLoggerPlugin : public LoggerPlugin {
- public:
- Status logString(const std::string& s);
- Status init(const std::string& name, const std::vector<StatusLogLine>& log);
- Status logStatus(const std::vector<StatusLogLine>& log);
-};
-
-REGISTER(SyslogLoggerPlugin, "logger", "syslog");
-
-Status SyslogLoggerPlugin::logString(const std::string& s) {
- for (const auto& line : osquery::split(s, "\n")) {
- syslog(LOG_INFO, "result=%s", line.c_str());
- }
- return Status(0, "OK");
-}
-
-Status SyslogLoggerPlugin::logStatus(const std::vector<StatusLogLine>& log) {
- for (const auto& item : log) {
- int severity = LOG_NOTICE;
- if (item.severity == O_INFO) {
- severity = LOG_NOTICE;
- } else if (item.severity == O_WARNING) {
- severity = LOG_WARNING;
- } else if (item.severity == O_ERROR) {
- severity = LOG_ERR;
- } else if (item.severity == O_FATAL) {
- severity = LOG_CRIT;
- }
-
- std::string line = "severity=" + std::to_string(item.severity)
- + " location=" + item.filename + ":" + std::to_string(item.line) +
- " message=" + item.message;
-
- syslog(severity, "%s", line.c_str());
- }
- return Status(0, "OK");
-}
-
-Status SyslogLoggerPlugin::init(const std::string& name,
- const std::vector<StatusLogLine>& log) {
- closelog();
-
- // Now funnel the intermediate status logs provided to `init`.
- return logStatus(log);
-}
-}
#include <iostream>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/filesystem.hpp>
#include <osquery/core.h>
#include <osquery/devtools/devtools.h>
-#include <osquery/filesystem/fileops.h>
#include <osquery/logger.h>
#include <osquery/main/main.h>
#include <osquery/registry_factory.h>
int startDaemon(Initializer& runner) {
runner.start();
-// osquery::events::init_syscall_tracing();
-
// Finally wait for a signal / interrupt to shutdown.
runner.waitForShutdown();
return 0;
*/
#include <osquery/core.h>
-#include <osquery/filesystem/filesystem.h>
#include <osquery/logger.h>
#include <osquery/sql.h>
#include "osquery/sql/dynamic_table_row.h"
#include "osquery/sql/sqlite_util.h"
+#include <boost/filesystem/path.hpp>
+
namespace fs = boost::filesystem;
+namespace errc = boost::system::errc;
namespace osquery {
TableRows& results,
bool respect_locking) {
sqlite3* db = nullptr;
- if (!pathExists(sqlite_db).ok()) {
+ boost::system::error_code ec;
+ if (sqlite_db.empty()) {
return Status(1, "Database path does not exist");
}
+ // A tri-state determination of presence
+ if (!fs::exists(sqlite_db, ec) || ec.value() != errc::success) {
+ return Status(1, ec.message());
+ }
+
auto rc = sqlite3_open_v2(
sqlite_db.string().c_str(),
&db,
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <map>
-#include <string>
-
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <boost/algorithm/string/predicate.hpp>
-#include <boost/algorithm/string/trim.hpp>
-#include <boost/noncopyable.hpp>
-#include <boost/regex.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/filesystem/linux/proc.h>
-#include <osquery/logger.h>
-#include <osquery/sql/dynamic_table_row.h>
-#include <osquery/tables.h>
-
-#include <osquery/utils/conversions/split.h>
-#include <osquery/utils/system/uptime.h>
-
-#include <ctime>
-
-namespace osquery {
-namespace tables {
-
-const int kMSIn1CLKTCK = (1000 / sysconf(_SC_CLK_TCK));
-
-inline std::string getProcAttr(const std::string& attr,
- const std::string& pid) {
- return "/proc/" + pid + "/" + attr;
-}
-
-inline std::string readProcCMDLine(const std::string& pid) {
- auto attr = getProcAttr("cmdline", pid);
-
- std::string content;
- readFile(attr, content);
- // Remove \0 delimiters.
- std::replace_if(content.begin(),
- content.end(),
- [](const char& c) { return c == 0; },
- ' ');
- // Remove trailing delimiter.
- boost::algorithm::trim(content);
- return content;
-}
-
-inline std::string readProcLink(const std::string& attr,
- const std::string& pid) {
- // The exe is a symlink to the binary on-disk.
- auto attr_path = getProcAttr(attr, pid);
-
- std::string result = "";
- struct stat sb;
- if (lstat(attr_path.c_str(), &sb) != -1) {
- // Some symlinks may report 'st_size' as zero
- // Use PATH_MAX as best guess
- // For cases when 'st_size' is not zero but smaller than
- // PATH_MAX we will still use PATH_MAX to minimize chance
- // of output trucation during race condition
- ssize_t buf_size = sb.st_size < PATH_MAX ? PATH_MAX : sb.st_size;
- // +1 for \0, since readlink does not append a null
- char* linkname = static_cast<char*>(malloc(buf_size + 1));
- ssize_t r = readlink(attr_path.c_str(), linkname, buf_size);
-
- if (r > 0) { // Success check
- // r may not be equal to buf_size
- // if r == buf_size there was race condition
- // and link is longer than buf_size and because of this
- // truncated
- linkname[r] = '\0';
- result = std::string(linkname);
- }
- free(linkname);
- }
-
- return result;
-}
-
-// In the case where the linked binary path ends in " (deleted)", and a file
-// actually exists at that path, check whether the inode of that file matches
-// the inode of the mapped file in /proc/%pid/maps
-Status deletedMatchesInode(const std::string& path, const std::string& pid) {
- const std::string maps_path = getProcAttr("maps", pid);
- std::string maps_contents;
- auto s = osquery::readFile(maps_path, maps_contents);
- if (!s.ok()) {
- return Status(-1, "Cannot read maps file: " + maps_path);
- }
-
- // Extract the expected inode of the binary file from /proc/%pid/maps
- boost::smatch what;
- boost::regex expression("([0-9]+)\\h+\\Q" + path + "\\E");
- if (!boost::regex_search(maps_contents, what, expression)) {
- return Status(-1, "Could not find binary inode in maps file: " + maps_path);
- }
- std::string inode = what[1];
-
- // stat the file at the expected binary path
- struct stat st;
- if (stat(path.c_str(), &st) != 0) {
- return Status(-1, "Error in stat of binary: " + path);
- }
-
- // If the inodes match, the binary name actually ends with " (deleted)"
- if (std::to_string(st.st_ino) == inode) {
- return Status::success();
- } else {
- return Status(1, "Inodes do not match");
- }
-}
-
-std::set<std::string> getProcList(const QueryContext& context) {
- std::set<std::string> pidlist;
- if (context.constraints.count("pid") > 0 &&
- context.constraints.at("pid").exists(EQUALS)) {
- for (const auto& pid : context.constraints.at("pid").getAll(EQUALS)) {
- if (isDirectory("/proc/" + pid)) {
- pidlist.insert(pid);
- }
- }
- } else {
- osquery::procProcesses(pidlist);
- }
-
- return pidlist;
-}
-
-void genProcessEnvironment(const std::string& pid, QueryData& results) {
- auto attr = getProcAttr("environ", pid);
-
- std::string content;
- readFile(attr, content);
- const char* variable = content.c_str();
-
- // Stop at the end of nul-delimited string content.
- while (*variable > 0) {
- auto buf = std::string(variable);
- size_t idx = buf.find_first_of("=");
-
- Row r;
- r["pid"] = pid;
- r["key"] = buf.substr(0, idx);
- r["value"] = buf.substr(idx + 1);
- results.push_back(r);
- variable += buf.size() + 1;
- }
-}
-
-void genProcessMap(const std::string& pid, QueryData& results) {
- auto map = getProcAttr("maps", pid);
-
- std::string content;
- readFile(map, content);
- for (auto& line : osquery::split(content, "\n")) {
- auto fields = osquery::split(line, " ");
- // If can't read address, not sure.
- if (fields.size() < 5) {
- continue;
- }
-
- Row r;
- r["pid"] = pid;
- if (!fields[0].empty()) {
- auto addresses = osquery::split(fields[0], "-");
- if (addresses.size() >= 2) {
- r["start"] = "0x" + addresses[0];
- r["end"] = "0x" + addresses[1];
- } else {
- // Problem with the address format.
- continue;
- }
- }
-
- r["permissions"] = fields[1];
- auto offset = tryTo<long long>(fields[2], 16);
- r["offset"] = BIGINT((offset) ? offset.take() : -1);
- r["device"] = fields[3];
- r["inode"] = fields[4];
-
- // Path name must be trimmed.
- if (fields.size() > 5) {
- boost::trim(fields[5]);
- r["path"] = fields[5];
- }
-
- // BSS with name in pathname.
- r["pseudo"] = (fields[4] == "0" && !r["path"].empty()) ? "1" : "0";
- results.push_back(std::move(r));
- }
-}
-
-/**
- * Output from string parsing /proc/<pid>/status.
- */
-struct SimpleProcStat : private boost::noncopyable {
- public:
- std::string name;
- std::string real_uid;
- std::string real_gid;
- std::string effective_uid;
- std::string effective_gid;
- std::string saved_uid;
- std::string saved_gid;
- std::string resident_size;
- std::string total_size;
- std::string state;
- std::string parent;
- std::string group;
- std::string nice;
- std::string threads;
- std::string user_time;
- std::string system_time;
- std::string start_time;
-
- /// For errors processing proc data.
- Status status;
-
- explicit SimpleProcStat(const std::string& pid);
-};
-
-SimpleProcStat::SimpleProcStat(const std::string& pid) {
- std::string content;
- if (readFile(getProcAttr("stat", pid), content).ok()) {
- auto start = content.find_last_of(")");
- // Start parsing stats from ") <MODE>..."
- if (start == std::string::npos || content.size() <= start + 2) {
- status = Status(1, "Invalid /proc/stat header");
- return;
- }
-
- auto details = osquery::split(content.substr(start + 2), " ");
- if (details.size() <= 19) {
- status = Status(1, "Invalid /proc/stat content");
- return;
- }
-
- this->state = details.at(0);
- this->parent = details.at(1);
- this->group = details.at(2);
- this->user_time = details.at(11);
- this->system_time = details.at(12);
- this->nice = details.at(16);
- this->threads = details.at(17);
- this->start_time = details.at(19);
- }
-
- // /proc/N/status may be not available, or readable by this user.
- if (!readFile(getProcAttr("status", pid), content).ok()) {
- status = Status(1, "Cannot read /proc/status");
- return;
- }
-
- for (const auto& line : osquery::split(content, "\n")) {
- // Status lines are formatted: Key: Value....\n.
- auto detail = osquery::split(line, ':', 1);
- if (detail.size() != 2) {
- continue;
- }
-
- // There are specific fields from each detail.
- if (detail.at(0) == "Name") {
- this->name = detail.at(1);
- } else if (detail.at(0) == "VmRSS") {
- detail[1].erase(detail.at(1).end() - 3, detail.at(1).end());
- // Memory is reported in kB.
- this->resident_size = detail.at(1) + "000";
- } else if (detail.at(0) == "VmSize") {
- detail[1].erase(detail.at(1).end() - 3, detail.at(1).end());
- // Memory is reported in kB.
- this->total_size = detail.at(1) + "000";
- } else if (detail.at(0) == "Gid") {
- // Format is: R E - -
- auto gid_detail = osquery::split(detail.at(1), "\t");
- if (gid_detail.size() == 4) {
- this->real_gid = gid_detail.at(0);
- this->effective_gid = gid_detail.at(1);
- this->saved_gid = gid_detail.at(2);
- }
- } else if (detail.at(0) == "Uid") {
- auto uid_detail = osquery::split(detail.at(1), "\t");
- if (uid_detail.size() == 4) {
- this->real_uid = uid_detail.at(0);
- this->effective_uid = uid_detail.at(1);
- this->saved_uid = uid_detail.at(2);
- }
- }
- }
-}
-
-/**
- * Output from string parsing /proc/<pid>/io.
- */
-struct SimpleProcIo : private boost::noncopyable {
- public:
- std::string read_bytes;
- std::string write_bytes;
- std::string cancelled_write_bytes;
-
- /// For errors processing proc data.
- Status status;
-
- explicit SimpleProcIo(const std::string& pid);
-};
-
-SimpleProcIo::SimpleProcIo(const std::string& pid) {
- std::string content;
- if (!readFile(getProcAttr("io", pid), content).ok()) {
- status = Status(
- 1, "Cannot read /proc/" + pid + "/io (is osquery running as root?)");
- return;
- }
-
- for (const auto& line : osquery::split(content, "\n")) {
- // IO lines are formatted: Key: Value....\n.
- auto detail = osquery::split(line, ':', 1);
- if (detail.size() != 2) {
- continue;
- }
-
- // There are specific fields from each detail
- if (detail.at(0) == "read_bytes") {
- this->read_bytes = detail.at(1);
- } else if (detail.at(0) == "write_bytes") {
- this->write_bytes = detail.at(1);
- } else if (detail.at(0) == "cancelled_write_bytes") {
- this->cancelled_write_bytes = detail.at(1);
- }
- }
-}
-
-/**
- * @brief Determine if the process path (binary) exists on the filesystem.
- *
- * 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.
- *
- * @param pid The string (because we're referencing file path) pid.
- * @param path A mutable string found from /proc/N/exe. If this is found
- * to contain the (deleted) suffix, it will be removed.
- * @return A tristate -1 error, 1 yes, 0 nope.
- */
-int getOnDisk(const std::string& pid, std::string& path) {
- if (path.empty()) {
- return -1;
- }
-
- // The string appended to the exe path when the binary is deleted
- const std::string kDeletedString = " (deleted)";
- if (!boost::algorithm::ends_with(path, kDeletedString)) {
- return (osquery::pathExists(path)) ? 1 : 0;
- }
-
- if (!osquery::pathExists(path)) {
- // No file exists with the path including " (deleted)", so we can strip
- // this from the path and set on_disk = 0
- path.erase(path.size() - kDeletedString.size());
- return 0;
- }
-
- // Special case in which we have to check the inode to see whether the
- // process is actually running from a binary file ending with
- // " (deleted)". See #1607
- std::string maps_contents;
- Status deleted = deletedMatchesInode(path, pid);
- if (deleted.getCode() == -1) {
- LOG(ERROR) << deleted.getMessage();
- return -1;
- } else if (deleted.getCode() == 0) {
- // The process is actually running from a binary ending with
- // " (deleted)"
- return 1;
- } else {
- // There is a collision with a file name ending in " (deleted)", but
- // that file is not the binary for this process
- path.erase(path.size() - kDeletedString.size());
- return 0;
- }
-}
-
-void genProcess(const std::string& pid,
- long system_boot_time,
- TableRows& results) {
- // Parse the process stat and status.
- SimpleProcStat proc_stat(pid);
- // Parse the process io
- SimpleProcIo proc_io(pid);
-
- if (!proc_stat.status.ok()) {
- VLOG(1) << proc_stat.status.getMessage() << " for pid " << pid;
- return;
- }
-
- auto r = make_table_row();
- r["pid"] = pid;
- r["parent"] = proc_stat.parent;
- r["path"] = readProcLink("exe", pid);
- r["name"] = proc_stat.name;
- r["pgroup"] = proc_stat.group;
- r["state"] = proc_stat.state;
- r["nice"] = proc_stat.nice;
- r["threads"] = proc_stat.threads;
- // Read/parse cmdline arguments.
- r["cmdline"] = readProcCMDLine(pid);
- r["cwd"] = readProcLink("cwd", pid);
- r["root"] = readProcLink("root", pid);
- r["uid"] = proc_stat.real_uid;
- r["euid"] = proc_stat.effective_uid;
- r["suid"] = proc_stat.saved_uid;
- r["gid"] = proc_stat.real_gid;
- r["egid"] = proc_stat.effective_gid;
- r["sgid"] = proc_stat.saved_gid;
-
- r["on_disk"] = INTEGER(getOnDisk(pid, r["path"]));
-
- // size/memory information
- r["wired_size"] = "0"; // No support for unpagable counters in linux.
- r["resident_size"] = proc_stat.resident_size;
- r["total_size"] = proc_stat.total_size;
-
- // time information
- auto usr_time = std::strtoull(proc_stat.user_time.data(), nullptr, 10);
- r["user_time"] = std::to_string(usr_time * kMSIn1CLKTCK);
- auto sys_time = std::strtoull(proc_stat.system_time.data(), nullptr, 10);
- r["system_time"] = std::to_string(sys_time * kMSIn1CLKTCK);
-
- auto proc_start_time_exp = tryTo<long>(proc_stat.start_time);
- if (proc_start_time_exp.isValue() && system_boot_time > 0) {
- r["start_time"] = INTEGER(system_boot_time + proc_start_time_exp.take() /
- sysconf(_SC_CLK_TCK));
- } else {
- r["start_time"] = "-1";
- }
-
- if (!proc_io.status.ok()) {
- // /proc/<pid>/io can require root to access, so don't fail if we can't
- VLOG(1) << proc_io.status.getMessage();
- } else {
- r["disk_bytes_read"] = proc_io.read_bytes;
- long long write_bytes = tryTo<long long>(proc_io.write_bytes).takeOr(0ll);
- long long cancelled_write_bytes =
- tryTo<long long>(proc_io.cancelled_write_bytes).takeOr(0ll);
-
- r["disk_bytes_written"] =
- std::to_string(write_bytes - cancelled_write_bytes);
- }
-
- results.push_back(r);
-}
-
-void genNamespaces(const std::string& pid, QueryData& results) {
- Row r;
-
- ProcessNamespaceList proc_ns;
- Status status = procGetProcessNamespaces(pid, proc_ns);
- if (!status.ok()) {
- VLOG(1) << "Namespaces for pid " << pid
- << " are incomplete: " << status.what();
- }
-
- r["pid"] = pid;
- for (const auto& pair : proc_ns) {
- r[pair.first + "_namespace"] = std::to_string(pair.second);
- }
-
- results.push_back(r);
-}
-
-TableRows genProcesses(QueryContext& context) {
- TableRows results;
- auto system_boot_time = getUptime();
- if (system_boot_time > 0) {
- system_boot_time = std::time(nullptr) - system_boot_time;
- }
-
- auto pidlist = getProcList(context);
- for (const auto& pid : pidlist) {
- genProcess(pid, system_boot_time, results);
- }
-
- return results;
-}
-
-QueryData genProcessEnvs(QueryContext& context) {
- QueryData results;
-
- auto pidlist = getProcList(context);
- for (const auto& pid : pidlist) {
- genProcessEnvironment(pid, results);
- }
-
- return results;
-}
-
-QueryData genProcessMemoryMap(QueryContext& context) {
- QueryData results;
-
- auto pidlist = getProcList(context);
- for (const auto& pid : pidlist) {
- genProcessMap(pid, results);
- }
-
- return results;
-}
-
-QueryData genProcessNamespaces(QueryContext& context) {
- QueryData results;
-
- const auto pidlist = getProcList(context);
- for (const auto& pid : pidlist) {
- genNamespaces(pid, results);
- }
-
- return results;
-}
-}
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#if !defined(WIN32)
-#include <sys/stat.h>
-#endif
-
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-#include <osquery/filesystem/fileops.h>
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-namespace tables {
-
-#if !defined(WIN32)
-
-const std::map<fs::file_type, std::string> kTypeNames{
- {fs::regular_file, "regular"},
- {fs::directory_file, "directory"},
- {fs::symlink_file, "symlink"},
- {fs::block_file, "block"},
- {fs::character_file, "character"},
- {fs::fifo_file, "fifo"},
- {fs::socket_file, "socket"},
- {fs::type_unknown, "unknown"},
- {fs::status_error, "error"},
-};
-
-#endif
-
-void genFileInfo(const fs::path& path,
- const fs::path& parent,
- const std::string& pattern,
- QueryData& results) {
- // Must provide the path, filename, directory separate from boost path->string
- // helpers to match any explicit (query-parsed) predicate constraints.
-
- Row r;
- r["path"] = path.string();
- r["filename"] = path.filename().string();
- r["directory"] = parent.string();
- r["symlink"] = "0";
-
-#if !defined(WIN32)
-
- struct stat file_stat;
-
- // On POSIX systems, first check the link state.
- struct stat link_stat;
- if (lstat(path.string().c_str(), &link_stat) < 0) {
- // Path was not real, had too may links, or could not be accessed.
- return;
- }
- if (S_ISLNK(link_stat.st_mode)) {
- r["symlink"] = "1";
- }
-
- if (stat(path.string().c_str(), &file_stat)) {
- file_stat = link_stat;
- }
-
- r["inode"] = BIGINT(file_stat.st_ino);
- r["uid"] = BIGINT(file_stat.st_uid);
- r["gid"] = BIGINT(file_stat.st_gid);
- r["mode"] = lsperms(file_stat.st_mode);
- r["device"] = BIGINT(file_stat.st_rdev);
- r["size"] = BIGINT(file_stat.st_size);
- r["block_size"] = INTEGER(file_stat.st_blksize);
- r["hard_links"] = INTEGER(file_stat.st_nlink);
-
- r["atime"] = BIGINT(file_stat.st_atime);
- r["mtime"] = BIGINT(file_stat.st_mtime);
- r["ctime"] = BIGINT(file_stat.st_ctime);
-
-#if defined(__linux__)
- // No 'birth' or create time in Linux or Windows.
- r["btime"] = "0";
-#else
- r["btime"] = BIGINT(file_stat.st_birthtimespec.tv_sec);
-#endif
-
- // Type booleans
- boost::system::error_code ec;
- auto status = fs::status(path, ec);
- if (kTypeNames.count(status.type())) {
- r["type"] = kTypeNames.at(status.type());
- } else {
- r["type"] = "unknown";
- }
-
-#else
-
- WINDOWS_STAT file_stat;
-
- auto rtn = platformStat(path, &file_stat);
- if (!rtn.ok()) {
- VLOG(1) << "PlatformStat failed with " << rtn.getMessage();
- return;
- }
-
- r["symlink"] = INTEGER(file_stat.symlink);
- r["inode"] = BIGINT(file_stat.inode);
- r["uid"] = BIGINT(file_stat.uid);
- r["gid"] = BIGINT(file_stat.gid);
- r["mode"] = TEXT(file_stat.mode);
- r["device"] = BIGINT(file_stat.device);
- r["size"] = BIGINT(file_stat.size);
- r["block_size"] = INTEGER(file_stat.block_size);
- r["hard_links"] = INTEGER(file_stat.hard_links);
- r["atime"] = BIGINT(file_stat.atime);
- r["mtime"] = BIGINT(file_stat.mtime);
- r["ctime"] = BIGINT(file_stat.ctime);
- r["btime"] = BIGINT(file_stat.btime);
- r["type"] = TEXT(file_stat.type);
- r["attributes"] = TEXT(file_stat.attributes);
- r["file_id"] = TEXT(file_stat.file_id);
- r["volume_serial"] = TEXT(file_stat.volume_serial);
- r["product_version"] = TEXT(file_stat.product_version);
-
-#endif
-
- results.push_back(r);
-}
-
-QueryData genFile(QueryContext& context) {
- QueryData results;
-
- // Resolve file paths for EQUALS and LIKE operations.
- auto paths = context.constraints["path"].getAll(EQUALS);
- context.expandConstraints(
- "path",
- LIKE,
- paths,
- ([&](const std::string& pattern, std::set<std::string>& out) {
- std::vector<std::string> patterns;
- auto status =
- resolveFilePattern(pattern, patterns, GLOB_ALL | GLOB_NO_CANON);
- if (status.ok()) {
- for (const auto& resolved : patterns) {
- out.insert(resolved);
- }
- }
- return status;
- }));
-
- // Iterate through each of the resolved/supplied paths.
- for (const auto& path_string : paths) {
- fs::path path = path_string;
- genFileInfo(path, path.parent_path(), "", results);
- }
-
- // Resolve directories for EQUALS and LIKE operations.
- auto directories = context.constraints["directory"].getAll(EQUALS);
- context.expandConstraints(
- "directory",
- LIKE,
- directories,
- ([&](const std::string& pattern, std::set<std::string>& out) {
- std::vector<std::string> patterns;
- auto status =
- resolveFilePattern(pattern, patterns, GLOB_FOLDERS | GLOB_NO_CANON);
- if (status.ok()) {
- for (const auto& resolved : patterns) {
- out.insert(resolved);
- }
- }
- return status;
- }));
-
- // Now loop through constraints using the directory column constraint.
- for (const auto& directory_string : directories) {
- if (!isReadable(directory_string) || !isDirectory(directory_string)) {
- continue;
- }
-
- try {
- // Iterate over the directory and generate info for each regular file.
- fs::directory_iterator begin(directory_string), end;
- for (; begin != end; ++begin) {
- genFileInfo(begin->path(), directory_string, "", results);
- }
- } catch (const fs::filesystem_error& /* e */) {
- continue;
- }
- }
-
- return results;
-}
-}
-} // namespace osquery
#include <vector>
#include <osquery/core.h>
-#include <osquery/filesystem/filesystem.h>
namespace osquery {
+++ /dev/null
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <string>
-
-namespace vist {
-namespace schema {
-
-struct Processes {
- long long int pid;
- std::string name;
- std::string path;
- std::string cmdline;
- long long int uid;
- long long int gid;
- long long int euid;
- long long int egid;
- int on_disk;
- long long int resident_size;
- long long int parent;
-};
-
-} // namesapce schema
-} // namesapce vist
#include <vist/client/virtual-table.hpp>
#include <vist/client/schema/policy.hpp>
-#include <vist/client/schema/processes.hpp>
#include <vist/client/schema/time.hpp>
#include <vist/logger.hpp>
EXPECT_NE(result.seconds, -1);
}
-TEST(VirtualTableTests, processes_table)
-{
- Processes result;
- VirtualTable<Processes> processes;
- EXPECT_TRUE(processes.size() > 0);
-
- for(auto& p : processes) {
- EXPECT_TRUE(p.size() > 0);
- result.pid = p.at(&Processes::pid);
- result.name = p.at(&Processes::name);
- result.path = p.at(&Processes::path);
- result.cmdline = p.at(&Processes::cmdline);
- result.uid = p.at(&Processes::uid);
- result.gid = p.at(&Processes::gid);
- result.euid = p.at(&Processes::euid);
- result.egid = p.at(&Processes::egid);
- result.on_disk = p.at(&Processes::on_disk);
- result.parent = p.at(&Processes::parent);
-
- INFO(VIST_CLIENT) << "[Test] Processes table:";
- INFO(VIST_CLIENT) << "\t pid: " << result.pid;
- INFO(VIST_CLIENT) << "\t name: " << result.name;
- INFO(VIST_CLIENT) << "\t path: " << result.path;
- INFO(VIST_CLIENT) << "\t cmdline: " << result.cmdline;
- INFO(VIST_CLIENT) << "\t uid: " << result.uid;
- INFO(VIST_CLIENT) << "\t gid: " << result.gid;
- INFO(VIST_CLIENT) << "\t euid: " << result.euid;
- INFO(VIST_CLIENT) << "\t egid: " << result.egid;
- INFO(VIST_CLIENT) << "\t on_disk: " << result.on_disk;
- INFO(VIST_CLIENT) << "\t parent: " << result.parent;
- }
-}
-
TEST(VirtualTableTests, policy_int_table)
{
VirtualTable<Policy<int>> table;
#include <vist/client/query.hpp>
#include <vist/client/schema/policy.hpp>
-#include <vist/client/schema/processes.hpp>
#include <vist/client/schema/time.hpp>
#include <vist/exception.hpp>
Column("minutes", &Time::minutes),
Column("seconds", &Time::seconds) };
-Table processes { "processes", Column("pid", &Processes::pid),
- Column("name", &Processes::name),
- Column("path", &Processes::path),
- Column("cmdline", &Processes::cmdline),
- Column("uid", &Processes::uid),
- Column("gid", &Processes::gid),
- Column("euid", &Processes::euid),
- Column("egid", &Processes::egid),
- Column("on_disk", &Processes::on_disk),
- Column("parent", &Processes::parent) };
-
Table policyInt { "policy", Column("name", &Policy<int>::name),
Column("value", &Policy<int>::value) };
Table policyStr { "policy", Column("name", &Policy<std::string>::name),
Column("value", &Policy<std::string>::value) };
-Database metaDB { "db", time, processes, policyInt, policyStr };
+Database metaDB { "db", time, policyInt, policyStr };
} // anonymous namespace
template int VirtualRow<Time>::at(int Time::*) const;
template int VirtualRow<Time>::operator[](int Time::*) const;
-template class VirtualTable<Processes>;
-template class VirtualRow<Processes>;
-template int VirtualRow<Processes>::at(int Processes::*) const;
-template int VirtualRow<Processes>::operator[](int Processes::*) const;
-template long long int VirtualRow<Processes>::at(long long int Processes::*) const;
-template long long int VirtualRow<Processes>::operator[](long long int Processes::*) const;
-template std::string VirtualRow<Processes>::at(std::string Processes::*) const;
-template std::string VirtualRow<Processes>::operator[](std::string Processes::*) const;
-
template class VirtualTable<Policy<int>>;
template class VirtualRow<Policy<int>>;
/// name column