osquery-1/osquery/tables/system/linux/os_version.cpp

143 lines
3.8 KiB
C++

/*
* Copyright (c) 2014-present, 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 <map>
#include <string>
#include <boost/algorithm/string/find.hpp>
#include <boost/algorithm/string/trim_all.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/xpressive/xpressive.hpp>
#include <osquery/filesystem.h>
#include <osquery/sql.h>
#include <osquery/tables.h>
#include "osquery/core/conversions.h"
namespace xp = boost::xpressive;
namespace osquery {
namespace tables {
const std::string kOSRelease = "/etc/os-release";
const std::string kRedhatRelease = "/etc/redhat-release";
const std::string kGentooRelease = "/etc/gentoo-release";
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;
if (!readFile(kOSRelease, content).ok()) {
return {};
}
Row r;
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)) {
boost::system::error_code ec;
// Funtoo has an empty os-release file.
if (boost::filesystem::file_size(kOSRelease, ec) > 0) {
return genOSRelease();
}
}
Row r;
std::string content;
if (readFile(kRedhatRelease, content).ok()) {
r["platform_like"] = "rhel";
} else if (readFile(kGentooRelease, content).ok()) {
r["platform_like"] = "gentoo";
} else {
return {};
}
boost::algorithm::trim_all(content);
// 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;
for (const auto& line : osquery::split(content, "\n")) {
if (xp::regex_search(line, matches, rx)) {
r["major"] = INTEGER(matches["major"]);
r["minor"] = INTEGER(matches["minor"]);
r["patch"] =
(matches["patch"].length() > 0) ? INTEGER(matches["patch"]) : "0";
r["name"] = matches["name"];
break;
}
}
r["version"] = content;
// No build name.
r["build"] = "";
if (r["platform"] == "") {
// Try to detect CentOS from the name. CentOS6 does not have all of the
// keys we expect above that platform is typically extracted from.
if (!boost::algorithm::ifind_first(r["name"], "centos").empty()) {
r["platform"] = "centos";
}
}
return {r};
}
}
}