Prefer /etc/os-release for Linux os_version (#2667)

This commit is contained in:
Teddy Reed 2016-10-22 16:58:32 -07:00 committed by GitHub
parent 5bb5ae1030
commit df25f27efb
3 changed files with 81 additions and 40 deletions

View File

@ -8,6 +8,7 @@
* *
*/ */
#include <map>
#include <string> #include <string>
#include <boost/xpressive/xpressive.hpp> #include <boost/xpressive/xpressive.hpp>
@ -23,38 +24,80 @@ namespace xp = boost::xpressive;
namespace osquery { namespace osquery {
namespace tables { namespace tables {
#if defined(REDHAT_BASED) const std::string kOSRelease = "/etc/os-release";
const std::string kLinuxOSRelease = "/etc/redhat-release"; const std::string kRedhatRelease = "/etc/redhat-release";
const std::string kLinuxOSRegex =
"(?P<name>[\\w+\\s]+) .* "
"(?P<major>[0-9]+)\\.(?P<minor>[0-9]+)\\.?(?P<patch>\\w+)?";
#elif defined(DEBIAN)
const std::string kLinuxOSRelease = "/etc/os-release";
const std::string kLinuxOSRegex =
"PRETTY_NAME=\"(?P<name>[\\w \\/]*) "
"(?P<major>[0-9]+)[\\.]{0,1}(?P<minor>[0-9]*)[\\.]{0,1}(?P<patch>[0-9]*).*"
"\"";
#else
const std::string kLinuxOSRelease = "/etc/os-release";
#ifdef UBUNTU_XENIAL
const std::string kLinuxOSRegex =
"VERSION=\"(?P<major>[0-9]+)\\.(?P<minor>[0-9]+)[\\.]{0,1}(?P<patch>[0-9]+)"
"?.* \\((?P<name>[\\w ]*)\\)\"$";
#else
const std::string kLinuxOSRegex =
"VERSION=\"(?P<major>[0-9]+)\\.(?P<minor>[0-9]+)[\\.]{0,1}(?P<patch>[0-9]+)"
"?.*, (?P<name>[\\w ]*)\"$";
#endif
#endif
QueryData genOSVersion(QueryContext& context) { const std::map<std::string, std::string> kOSReleaseColumns = {
{"NAME", "name"},
{"VERSION", "version"},
{"BUILD_ID", "build"},
{"ID", "platform"},
{"ID_LIKE", "platform_like"},
{"VERSION_CODENAME", "codename"},
{"VERSION_ID", "_id"},
};
QueryData genOSRelease() {
// This will parse /etc/os-version according to the systemd manual.
std::string content; std::string content;
if (!forensicReadFile(kLinuxOSRelease, content).ok()) { if (!readFile(kOSRelease, content).ok()) {
return {}; return {};
} }
Row r; Row r;
auto rx = xp::sregex::compile(kLinuxOSRegex); for (const auto& line : osquery::split(content, "\n")) {
auto fields = osquery::split(line, "=", 1);
if (fields.size() != 2) {
continue;
}
auto column = std::ref(kOSReleaseColumns.at("VERSION_CODENAME"));
if (kOSReleaseColumns.count(fields[0]) != 0) {
column = std::ref(kOSReleaseColumns.at(fields[0]));
} else if (fields[0].find("CODENAME") == std::string::npos) {
// Some distros may attach/invent their own CODENAME field.
continue;
}
r[column] = std::move(fields[1]);
if (!r.at(column).empty() && r.at(column)[0] == '"') {
// This is quote-enclosed string, make it pretty!
r[column] = r[column].substr(1, r.at(column).size() - 2);
}
if (column.get() == "_id") {
auto parts = osquery::split(r.at(column), ".", 2);
switch (parts.size()) {
case 3:
r["patch"] = parts[2];
case 2:
r["minor"] = parts[1];
case 1:
r["major"] = parts[0];
break;
}
}
}
return {r};
}
QueryData genOSVersion(QueryContext& context) {
if (isReadable(kOSRelease)) {
return genOSRelease();
}
std::string content;
if (!isReadable(kRedhatRelease) || !readFile(kRedhatRelease, content).ok()) {
// This is an unknown Linux OS.
return {};
}
Row r;
// This is an older version of a Redhat-based OS.
auto rx = xp::sregex::compile(
"(?P<name>[\\w+\\s]+) .* "
"(?P<major>[0-9]+)\\.(?P<minor>[0-9]+)\\.?(?P<patch>\\w+)?");
xp::smatch matches; xp::smatch matches;
for (const auto& line : osquery::split(content, "\n")) { for (const auto& line : osquery::split(content, "\n")) {
if (xp::regex_search(line, matches, rx)) { if (xp::regex_search(line, matches, rx)) {
@ -67,6 +110,9 @@ QueryData genOSVersion(QueryContext& context) {
} }
} }
r["version"] = content;
r["platform_like"] = "rhel";
// No build name. // No build name.
r["build"] = ""; r["build"] = "";
return {r}; return {r};

View File

@ -26,22 +26,13 @@ class SystemsTablesTests : public testing::Test {};
TEST_F(SystemsTablesTests, test_os_version) { TEST_F(SystemsTablesTests, test_os_version) {
auto results = SQL("select * from os_version"); auto results = SQL("select * from os_version");
// Issue #2564: There is no os_version on Windows.
if (!isPlatform(PlatformType::TYPE_WINDOWS)) {
EXPECT_EQ(results.rows().size(), 1U); EXPECT_EQ(results.rows().size(), 1U);
// Make sure major and minor have data (a missing value of -1 is an error). // Make sure major and minor have data (a missing value of -1 is an error).
EXPECT_FALSE(results.rows()[0].at("major").empty()); EXPECT_FALSE(results.rows()[0].at("major").empty());
// Debian does not define a minor.
#if !defined(DEBIAN)
EXPECT_FALSE(results.rows()[0].at("minor").empty());
#endif
// The OS name should be filled in too. // The OS name should be filled in too.
EXPECT_FALSE(results.rows()[0].at("name").empty()); EXPECT_FALSE(results.rows()[0].at("name").empty());
} }
}
TEST_F(SystemsTablesTests, test_process_info) { TEST_F(SystemsTablesTests, test_process_info) {
auto results = SQL("select * from osquery_info join processes using (pid)"); auto results = SQL("select * from osquery_info join processes using (pid)");

View File

@ -2,10 +2,14 @@ table_name("os_version")
description("A single row containing the operating system name and version.") description("A single row containing the operating system name and version.")
schema([ schema([
Column("name", TEXT, "Distribution or product name"), Column("name", TEXT, "Distribution or product name"),
Column("version", TEXT, "Pretty, suitable for presentation, OS version"),
Column("major", INTEGER, "Major release version"), Column("major", INTEGER, "Major release version"),
Column("minor", INTEGER, "Minor release version"), Column("minor", INTEGER, "Minor release version"),
Column("patch", INTEGER, "Optional patch release"), Column("patch", INTEGER, "Optional patch release"),
Column("build", TEXT, "Optional build-specific or variant string"), Column("build", TEXT, "Optional build-specific or variant string"),
Column("platform", TEXT, "OS Platform or ID"),
Column("platform_like", TEXT, "Closely related platforms"),
Column("codename", TEXT, "OS version codename"),
]) ])
implementation("system/os_version@genOSVersion") implementation("system/os_version@genOSVersion")
fuzz_paths([ fuzz_paths([