mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-06 09:35:20 +00:00
selinux_settings: New table that presents effective SELinux settings (#6118)
* selinux_settings: New table that presents effective SELinux settings * selinux_settings: Use the SELinux root path from the mounted fs The code that was originally directly implemented inside the `mounts` table has been moved outside so that it can be reused by the selinux_settings table. This also updates the code to use getmntent_r instead of getmntent.
This commit is contained in:
parent
0b2aa61a7d
commit
8d9059f914
@ -25,6 +25,7 @@ osquery_cxx_library(
|
||||
LINUX,
|
||||
[
|
||||
"linux/proc.h",
|
||||
"linux/mounts.h",
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -55,6 +56,7 @@ osquery_cxx_library(
|
||||
[
|
||||
"linux/mem.cpp",
|
||||
"linux/proc.cpp",
|
||||
"linux/mounts.cpp",
|
||||
],
|
||||
),
|
||||
(
|
||||
@ -74,6 +76,7 @@ osquery_cxx_library(
|
||||
osquery_target("osquery/utils/conversions:conversions"),
|
||||
osquery_target("osquery/utils/status:status"),
|
||||
osquery_target("osquery/utils/system:env"),
|
||||
osquery_target("osquery/utils/system:filepath"),
|
||||
osquery_tp_target("boost"),
|
||||
osquery_tp_target("libarchive"),
|
||||
osquery_tp_target("zstd"),
|
||||
|
@ -36,6 +36,7 @@ function(generateOsqueryFilesystem)
|
||||
list(APPEND source_files
|
||||
linux/mem.cpp
|
||||
linux/proc.cpp
|
||||
linux/mounts.cpp
|
||||
)
|
||||
|
||||
elseif(DEFINED PLATFORM_WINDOWS)
|
||||
@ -55,6 +56,7 @@ function(generateOsqueryFilesystem)
|
||||
osquery_utils_conversions
|
||||
osquery_utils_status
|
||||
osquery_utils_system_env
|
||||
osquery_utils_system_filepath
|
||||
thirdparty_boost
|
||||
thirdparty_libarchive
|
||||
thirdparty_zstd
|
||||
@ -68,6 +70,7 @@ function(generateOsqueryFilesystem)
|
||||
if(DEFINED PLATFORM_LINUX)
|
||||
list(APPEND public_header_files
|
||||
linux/proc.h
|
||||
linux/mounts.h
|
||||
)
|
||||
endif()
|
||||
|
||||
|
113
osquery/filesystem/linux/mounts.cpp
Normal file
113
osquery/filesystem/linux/mounts.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* 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 <mntent.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#include <osquery/filesystem/linux/mounts.h>
|
||||
#include <osquery/logger.h>
|
||||
#include <osquery/utils/system/filepath.h>
|
||||
|
||||
namespace osquery {
|
||||
namespace {
|
||||
const std::string kMountsPseudoFile{"/proc/mounts"};
|
||||
|
||||
struct MountDataDeleter final {
|
||||
void operator()(FILE* ptr) {
|
||||
if (ptr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
endmntent(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
using MountData = std::unique_ptr<FILE, MountDataDeleter>;
|
||||
|
||||
Status getMountData(MountData& obj) {
|
||||
obj = {};
|
||||
|
||||
auto mount_data = setmntent(kMountsPseudoFile.c_str(), "r");
|
||||
if (mount_data == nullptr) {
|
||||
return Status::failure("Failed to open the '" + kMountsPseudoFile +
|
||||
"' pseudo file");
|
||||
}
|
||||
|
||||
obj.reset(mount_data);
|
||||
return Status::success();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status getMountedFilesystemMap(MountedFilesystemMap& mounted_fs_info) {
|
||||
mounted_fs_info = {};
|
||||
|
||||
MountData mount_data;
|
||||
auto status = getMountData(mount_data);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
std::vector<char> string_buffer(4096);
|
||||
|
||||
for (;;) {
|
||||
mntent ent = {};
|
||||
if (getmntent_r(mount_data.get(),
|
||||
&ent,
|
||||
string_buffer.data(),
|
||||
string_buffer.size()) == nullptr) {
|
||||
if (errno != ENOENT) {
|
||||
LOG(ERROR) << "getmntent_r failed with errno " << std::to_string(errno);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
MountInformation mount_info = {};
|
||||
mount_info.type = ent.mnt_type;
|
||||
mount_info.device = ent.mnt_fsname;
|
||||
mount_info.device_alias = canonicalize_file_name(ent.mnt_fsname);
|
||||
mount_info.path = ent.mnt_dir;
|
||||
mount_info.flags = ent.mnt_opts;
|
||||
|
||||
if (mount_info.type == "autofs") {
|
||||
VLOG(1) << "Skipping statfs information for autofs mount: "
|
||||
<< mount_info.path;
|
||||
|
||||
} else {
|
||||
struct statfs stats = {};
|
||||
if (statfs(mount_info.path.c_str(), &stats) == 0) {
|
||||
MountInformation::StatFsInfo statfs_info = {};
|
||||
|
||||
statfs_info.block_size = static_cast<std::uint32_t>(stats.f_bsize);
|
||||
statfs_info.block_count = static_cast<std::uint32_t>(stats.f_blocks);
|
||||
|
||||
statfs_info.free_block_count =
|
||||
static_cast<std::uint32_t>(stats.f_bfree);
|
||||
|
||||
statfs_info.unprivileged_free_block_count =
|
||||
static_cast<std::uint32_t>(stats.f_bavail);
|
||||
|
||||
statfs_info.inode_count = static_cast<std::uint32_t>(stats.f_files);
|
||||
|
||||
statfs_info.free_inode_count =
|
||||
static_cast<std::uint32_t>(stats.f_ffree);
|
||||
|
||||
mount_info.optional_statfs_info = std::move(statfs_info);
|
||||
|
||||
} else {
|
||||
LOG(ERROR) << "statfs failed with errno " << std::to_string(errno)
|
||||
<< " on path " << mount_info.path;
|
||||
}
|
||||
}
|
||||
|
||||
mounted_fs_info.insert({mount_info.path, std::move(mount_info)});
|
||||
}
|
||||
|
||||
return Status::success();
|
||||
}
|
||||
} // namespace osquery
|
62
osquery/filesystem/linux/mounts.h
Normal file
62
osquery/filesystem/linux/mounts.h
Normal file
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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 <boost/optional.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <osquery/core.h>
|
||||
#include <osquery/filesystem/filesystem.h>
|
||||
|
||||
namespace osquery {
|
||||
// Information about a single mounted filesystem
|
||||
struct MountInformation final {
|
||||
struct StatFsInfo final {
|
||||
// Optimal transfer block size (statfs::f_bsize)
|
||||
std::uint32_t block_size{0U};
|
||||
|
||||
// Total data blocks in file system (statfs::f_blocks)
|
||||
std::uint32_t block_count{0U};
|
||||
|
||||
// Free blocks in filesystem (statfs::f_bfree)
|
||||
std::uint32_t free_block_count{0U};
|
||||
|
||||
// Free blocks available to unprivileged user (statfs::f_bavail)
|
||||
std::uint32_t unprivileged_free_block_count{0U};
|
||||
|
||||
// Total file nodes in filesystem (statfs::f_files)
|
||||
std::uint32_t inode_count{0U};
|
||||
|
||||
// Free file nodes in filesystem (statfs::f_ffree)
|
||||
std::uint32_t free_inode_count{0U};
|
||||
};
|
||||
|
||||
// Filesystem type
|
||||
std::string type;
|
||||
|
||||
// Device path
|
||||
std::string device;
|
||||
|
||||
// Canonicalized device path
|
||||
std::string device_alias;
|
||||
|
||||
// Mount path
|
||||
std::string path;
|
||||
|
||||
// Mount options
|
||||
std::string flags;
|
||||
|
||||
// statfs information; may not be set if the statfs operation
|
||||
// has failed
|
||||
boost::optional<StatFsInfo> optional_statfs_info;
|
||||
};
|
||||
|
||||
// Information about all mounted filesystems
|
||||
using MountedFilesystemMap = std::unordered_map<std::string, MountInformation>;
|
||||
|
||||
Status getMountedFilesystemMap(MountedFilesystemMap& mounted_fs_info);
|
||||
} // namespace osquery
|
@ -157,6 +157,7 @@ osquery_cxx_library(
|
||||
"linux/process_open_files.cpp",
|
||||
"linux/processes.cpp",
|
||||
"linux/rpm_packages.cpp",
|
||||
"linux/selinux_settings.cpp",
|
||||
"linux/shadow.cpp",
|
||||
"linux/shared_memory.cpp",
|
||||
"linux/smbios_tables.cpp",
|
||||
|
@ -78,6 +78,7 @@ function(generateOsqueryTablesSystemSystemtable)
|
||||
linux/usb_devices.cpp
|
||||
linux/user_groups.cpp
|
||||
linux/users.cpp
|
||||
linux/selinux_settings.cpp
|
||||
)
|
||||
|
||||
elseif(DEFINED PLATFORM_MACOS)
|
||||
|
@ -6,57 +6,58 @@
|
||||
* the LICENSE file found in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <mntent.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <osquery/core.h>
|
||||
#include <osquery/filesystem/filesystem.h>
|
||||
#include <osquery/filesystem/linux/mounts.h>
|
||||
#include <osquery/logger.h>
|
||||
#include <osquery/tables.h>
|
||||
#include <osquery/utils/system/filepath.h>
|
||||
|
||||
namespace osquery {
|
||||
namespace tables {
|
||||
|
||||
std::set<std::string> kMountStatBlacklist = {
|
||||
"autofs",
|
||||
};
|
||||
|
||||
QueryData genMounts(QueryContext& context) {
|
||||
QueryData results;
|
||||
|
||||
FILE* mounts = setmntent("/proc/mounts", "r");
|
||||
if (mounts == nullptr) {
|
||||
MountedFilesystemMap mounted_fs_map{};
|
||||
auto status = getMountedFilesystemMap(mounted_fs_map);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Failed to list the system mounts: " << status.getMessage();
|
||||
return {};
|
||||
}
|
||||
|
||||
struct mntent* ent = nullptr;
|
||||
while ((ent = getmntent(mounts))) {
|
||||
Row r;
|
||||
QueryData results;
|
||||
|
||||
r["type"] = std::string(ent->mnt_type);
|
||||
r["device"] = std::string(ent->mnt_fsname);
|
||||
r["device_alias"] = canonicalize_file_name(ent->mnt_fsname);
|
||||
r["path"] = std::string(ent->mnt_dir);
|
||||
r["flags"] = std::string(ent->mnt_opts);
|
||||
for (const auto& p : mounted_fs_map) {
|
||||
Row r = {};
|
||||
|
||||
// Check type against blacklist before running statfs.
|
||||
if (kMountStatBlacklist.find(r["type"]) == kMountStatBlacklist.end()) {
|
||||
struct statfs st;
|
||||
if (!statfs(ent->mnt_dir, &st)) {
|
||||
r["blocks_size"] = BIGINT(st.f_bsize);
|
||||
r["blocks"] = BIGINT(st.f_blocks);
|
||||
r["blocks_free"] = BIGINT(st.f_bfree);
|
||||
r["blocks_available"] = BIGINT(st.f_bavail);
|
||||
r["inodes"] = BIGINT(st.f_files);
|
||||
r["inodes_free"] = BIGINT(st.f_ffree);
|
||||
}
|
||||
const auto& mount_info = p.second;
|
||||
|
||||
r["type"] = mount_info.type;
|
||||
r["device"] = mount_info.device;
|
||||
r["device_alias"] = mount_info.device_alias;
|
||||
r["path"] = mount_info.path;
|
||||
r["flags"] = mount_info.flags;
|
||||
|
||||
// optional::has_value is not present in Boost 1.66 (which is
|
||||
// what we have when compiling with BUCK)
|
||||
if (mount_info.optional_statfs_info != boost::none) {
|
||||
const auto& statfs_info = mount_info.optional_statfs_info.value();
|
||||
|
||||
r["blocks_size"] = BIGINT(statfs_info.block_size);
|
||||
r["blocks"] = BIGINT(statfs_info.block_count);
|
||||
r["blocks_free"] = BIGINT(statfs_info.free_block_count);
|
||||
|
||||
r["blocks_available"] = BIGINT(statfs_info.unprivileged_free_block_count);
|
||||
|
||||
r["inodes"] = BIGINT(statfs_info.inode_count);
|
||||
r["inodes_free"] = BIGINT(statfs_info.free_inode_count);
|
||||
|
||||
} else {
|
||||
r["blocks_size"] = BIGINT(0);
|
||||
r["blocks"] = BIGINT(0);
|
||||
r["blocks_free"] = BIGINT(0);
|
||||
r["blocks_available"] = BIGINT(0);
|
||||
r["inodes"] = BIGINT(0);
|
||||
r["inodes_free"] = BIGINT(0);
|
||||
}
|
||||
|
||||
results.push_back(std::move(r));
|
||||
}
|
||||
endmntent(mounts);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
274
osquery/tables/system/linux/selinux_settings.cpp
Normal file
274
osquery/tables/system/linux/selinux_settings.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
/**
|
||||
* 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/core.h>
|
||||
#include <osquery/filesystem/filesystem.h>
|
||||
#include <osquery/filesystem/linux/mounts.h>
|
||||
#include <osquery/logger.h>
|
||||
#include <osquery/tables.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace osquery {
|
||||
namespace tables {
|
||||
Status keyNameFromFilePath(std::string& key_name,
|
||||
const std::string& selinuxfs_path,
|
||||
const std::string& file_path);
|
||||
|
||||
Status translateBooleanKeyValue(std::string& value,
|
||||
const std::string& raw_value);
|
||||
|
||||
namespace {
|
||||
Status getSelinuxfsMountPath(std::string& path) {
|
||||
path = {};
|
||||
|
||||
MountedFilesystemMap mounted_fs_map{};
|
||||
auto status = getMountedFilesystemMap(mounted_fs_map);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
auto selinuxfs_info_it = std::find_if(
|
||||
mounted_fs_map.begin(),
|
||||
mounted_fs_map.end(),
|
||||
|
||||
[](const std::pair<std::string, MountInformation> &p) -> bool {
|
||||
const auto &fs_type = p.second.type;
|
||||
return (fs_type == "selinuxfs");
|
||||
}
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
if (selinuxfs_info_it != mounted_fs_map.end()) {
|
||||
path = selinuxfs_info_it->second.path;
|
||||
}
|
||||
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
const std::vector<std::string> kRootKeyList = {"checkreqprot",
|
||||
"deny_unknown",
|
||||
"enforce",
|
||||
"mls",
|
||||
"policyvers",
|
||||
"reject_unknown"};
|
||||
|
||||
const std::vector<std::string> kScopeList = {
|
||||
"booleans", "policy_capabilities", "initial_contexts"};
|
||||
|
||||
Status generateScopeKey(Row& row,
|
||||
const std::string& scope,
|
||||
const std::string& selinuxfs_path,
|
||||
const std::string& key) {
|
||||
row = {};
|
||||
|
||||
row["scope"] = scope;
|
||||
row["key"] = key;
|
||||
|
||||
auto path = selinuxfs_path + "/" + scope + "/" + key;
|
||||
|
||||
std::string raw_value;
|
||||
auto status = readFile(path, raw_value);
|
||||
if (!status.ok()) {
|
||||
return Status::failure("Failed to retrieve SELinux key value: " + path +
|
||||
". Error: " + status.getMessage());
|
||||
}
|
||||
|
||||
std::string value;
|
||||
if (scope == "booleans") {
|
||||
status = translateBooleanKeyValue(value, raw_value);
|
||||
if (!status.ok()) {
|
||||
return Status::failure("Failed to retrieve SELinux key value: " + path +
|
||||
". Error: " + status.getMessage());
|
||||
}
|
||||
|
||||
} else {
|
||||
value = std::move(raw_value);
|
||||
}
|
||||
|
||||
row["value"] = std::move(value);
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status generateScope(QueryData& row_list,
|
||||
const std::string& selinuxfs_path,
|
||||
const std::string& scope) {
|
||||
auto scope_directory_path = selinuxfs_path + "/" + scope;
|
||||
|
||||
std::vector<std::string> path_list;
|
||||
auto status = listFilesInDirectory(scope_directory_path, path_list, true);
|
||||
if (!status.ok()) {
|
||||
return Status::failure("Failed to enumerate the files in '" +
|
||||
scope_directory_path + "': " + status.getMessage());
|
||||
}
|
||||
|
||||
for (const auto& path : path_list) {
|
||||
if (isDirectory(path).ok()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string key_name = {};
|
||||
status = keyNameFromFilePath(key_name, selinuxfs_path, path);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Invalid SELinux key path '" + path
|
||||
<< "'. Error: " << status.getMessage();
|
||||
continue;
|
||||
}
|
||||
|
||||
Row row;
|
||||
status = generateScopeKey(row, scope, selinuxfs_path, key_name);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << status.getMessage();
|
||||
continue;
|
||||
}
|
||||
|
||||
row_list.push_back(std::move(row));
|
||||
}
|
||||
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status generateClasses(QueryData& row_list, const std::string& selinuxfs_path) {
|
||||
auto class_root_path = selinuxfs_path + "/class";
|
||||
|
||||
std::vector<std::string> path_list;
|
||||
auto status = listFilesInDirectory(class_root_path, path_list, true);
|
||||
if (!status.ok()) {
|
||||
return Status::failure(
|
||||
"Failed to enumerate the SELinux settings from under '" +
|
||||
class_root_path + "': " + status.getMessage());
|
||||
}
|
||||
|
||||
for (const auto& path : path_list) {
|
||||
if (isDirectory(path).ok()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto value_index = path.find_last_of('/');
|
||||
if (value_index == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++value_index;
|
||||
if (value_index >= path.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto value = path.substr(value_index);
|
||||
if (value == "index") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto key_index = class_root_path.size() + 1U;
|
||||
auto key_length = value_index - key_index - 1U;
|
||||
auto key = path.substr(key_index, key_length);
|
||||
|
||||
Row row = {};
|
||||
row["scope"] = "class";
|
||||
row["key"] = key;
|
||||
row["value"] = value;
|
||||
|
||||
row_list.push_back(std::move(row));
|
||||
}
|
||||
|
||||
return Status::success();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status keyNameFromFilePath(std::string& key_name,
|
||||
const std::string& selinuxfs_path,
|
||||
const std::string& file_path) {
|
||||
// This limit only applies to this specific case and not to the
|
||||
// items in the 'class' scope
|
||||
static const std::size_t kMinimumPathSize = selinuxfs_path.size() + 2U;
|
||||
|
||||
key_name = {};
|
||||
|
||||
if (file_path.find(selinuxfs_path) != 0) {
|
||||
return Status::failure("The given path is outside the SELinux folder");
|
||||
}
|
||||
|
||||
if (file_path.size() <= kMinimumPathSize) {
|
||||
return Status::failure("The given path is too small");
|
||||
}
|
||||
|
||||
auto key_name_index = file_path.find_last_of('/');
|
||||
if (key_name_index == std::string::npos ||
|
||||
key_name_index + 1 >= file_path.size()) {
|
||||
return Status::failure("Invalid path specified");
|
||||
}
|
||||
|
||||
++key_name_index;
|
||||
|
||||
key_name = file_path.substr(key_name_index);
|
||||
if (key_name.empty()) {
|
||||
return Status::failure("Key name is empty");
|
||||
}
|
||||
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status translateBooleanKeyValue(std::string& value,
|
||||
const std::string& raw_value) {
|
||||
value = {};
|
||||
|
||||
if (raw_value == "0 0") {
|
||||
value = "off";
|
||||
|
||||
} else if (raw_value == "1 1") {
|
||||
value = "on";
|
||||
|
||||
} else {
|
||||
return Status::failure("Invalid raw value for boolean key");
|
||||
}
|
||||
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
QueryData genSELinuxSettings(QueryContext& context) {
|
||||
std::string selinuxfs_path;
|
||||
auto status = getSelinuxfsMountPath(selinuxfs_path);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Failed to acquire the SELinux FS path"
|
||||
<< status.getMessage();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (selinuxfs_path.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QueryData row_list;
|
||||
for (const auto& root_key : kRootKeyList) {
|
||||
Row row;
|
||||
status = generateScopeKey(row, "", selinuxfs_path, root_key);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Failed to generate SELinux root key: "
|
||||
<< status.getMessage();
|
||||
}
|
||||
|
||||
row_list.push_back(std::move(row));
|
||||
}
|
||||
|
||||
for (const auto& scope : kScopeList) {
|
||||
status = generateScope(row_list, selinuxfs_path, scope);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Failed to generate SELinux scope: " << status.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
status = generateClasses(row_list, selinuxfs_path);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "failed to bla bla";
|
||||
}
|
||||
|
||||
return row_list;
|
||||
}
|
||||
} // namespace tables
|
||||
} // namespace osquery
|
@ -18,6 +18,7 @@ osquery_cxx_test(
|
||||
"linux/pci_devices_tests.cpp",
|
||||
"linux/pcidb_tests.cpp",
|
||||
"linux/portage_tests.cpp",
|
||||
"linux/selinux_settings_tests.cpp"
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -32,6 +32,7 @@ function(generateOsqueryTablesSystemLinuxTests)
|
||||
linux/pci_devices_tests.cpp
|
||||
linux/pcidb_tests.cpp
|
||||
linux/portage_tests.cpp
|
||||
linux/selinux_settings_tests.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(osquery_tables_system_linux_tests-test PRIVATE
|
||||
|
79
osquery/tables/system/tests/linux/selinux_settings_tests.cpp
Normal file
79
osquery/tables/system/tests/linux/selinux_settings_tests.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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 <pwd.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <osquery/core.h>
|
||||
#include <osquery/logger.h>
|
||||
#include <osquery/system.h>
|
||||
#include <osquery/utils/status/status.h>
|
||||
|
||||
namespace osquery {
|
||||
namespace tables {
|
||||
Status keyNameFromFilePath(std::string& key_name,
|
||||
const std::string& selinuxfs_path,
|
||||
const std::string& file_path);
|
||||
|
||||
bool isBooleanKey(const std::string& key_name);
|
||||
|
||||
Status translateBooleanKeyValue(std::string& value,
|
||||
const std::string& raw_value);
|
||||
|
||||
class SELinuxSettingsTests : public testing::Test {};
|
||||
|
||||
TEST_F(SELinuxSettingsTests, keyNameFromFilePath) {
|
||||
static const std::string kSelinuxFsPath{"/sys/fs/selinux"};
|
||||
|
||||
struct TestCase final {
|
||||
bool expected_status{false};
|
||||
std::string expected_key_name;
|
||||
std::string file_path;
|
||||
};
|
||||
|
||||
const std::vector<TestCase> test_case_list = {
|
||||
{false, "", kSelinuxFsPath},
|
||||
{false, "", kSelinuxFsPath + "/"},
|
||||
{true, "devnull", kSelinuxFsPath + "/initial_contexts/devnull"},
|
||||
{true, "name_bind", kSelinuxFsPath + "/class/smc_socket/perms/name_bind"},
|
||||
{true,
|
||||
"nnp_nosuid_transition",
|
||||
kSelinuxFsPath + "/policy_capabilities/nnp_nosuid_transition"}};
|
||||
|
||||
for (const auto& test_case : test_case_list) {
|
||||
std::string key_name;
|
||||
auto status =
|
||||
keyNameFromFilePath(key_name, kSelinuxFsPath, test_case.file_path);
|
||||
|
||||
CHECK_EQ(key_name, test_case.expected_key_name);
|
||||
ASSERT_EQ(status.ok(), test_case.expected_status);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SELinuxSettingsTests, translateBooleanKeyValue) {
|
||||
struct TestCase final {
|
||||
bool expected_status{false};
|
||||
std::string expected_value;
|
||||
std::string raw_value;
|
||||
};
|
||||
|
||||
const std::vector<TestCase> test_case_list = {
|
||||
{true, "on", "1 1"}, {true, "off", "0 0"}, {false, "", "0"}};
|
||||
|
||||
for (const auto& test_case : test_case_list) {
|
||||
std::string translated_value;
|
||||
auto status =
|
||||
translateBooleanKeyValue(translated_value, test_case.raw_value);
|
||||
|
||||
ASSERT_EQ(status.ok(), test_case.expected_status);
|
||||
ASSERT_EQ(translated_value, test_case.expected_value);
|
||||
}
|
||||
}
|
||||
} // namespace tables
|
||||
} // namespace osquery
|
@ -162,6 +162,7 @@ function(generateNativeTables)
|
||||
"linux/elf_dynamic.table:linux"
|
||||
"linux/ec2_instance_metadata.table:linux"
|
||||
"linux/elf_sections.table:linux"
|
||||
"linux/selinux_settings.table:linux"
|
||||
"linwin/intel_me_info.table:linux,windows"
|
||||
"lldpd/lldp_neighbors.table:linux,macos,freebsd"
|
||||
"kernel_info.table:linux,macos,windows"
|
||||
|
11
specs/linux/selinux_settings.table
Normal file
11
specs/linux/selinux_settings.table
Normal file
@ -0,0 +1,11 @@
|
||||
table_name("selinux_settings")
|
||||
description("Track active SELinux settings.")
|
||||
schema([
|
||||
Column("scope", TEXT, "Where the key is located inside the SELinuxFS mount point.", index=True),
|
||||
Column("key", TEXT, "Key or class name.", index=True),
|
||||
Column("value", TEXT, "Active value."),
|
||||
])
|
||||
implementation("system/selinux_settings@genSELinuxSettings")
|
||||
examples([
|
||||
"SELECT * FROM selinux_settings WHERE key = 'enforce'",
|
||||
])
|
Loading…
Reference in New Issue
Block a user