Adding new table to display iptables filters, chains and rules

Patching headers to avoid void pointers
Adding test for parsing ipt_ip entries
This commit is contained in:
Javier Marcos 2015-04-27 17:41:58 -07:00 committed by Teddy Reed
parent 3294929226
commit 4f21090fb8
8 changed files with 288 additions and 0 deletions

View File

@ -57,6 +57,7 @@ else()
ADD_OSQUERY_LINK(FALSE "cryptsetup")
ADD_OSQUERY_LINK(FALSE "udev")
ADD_OSQUERY_LINK(FALSE "uuid")
ADD_OSQUERY_LINK(FALSE "ip4tc")
endif()
file(GLOB OSQUERY_CROSS_TABLES "[!ue]*/*.cpp")

View File

@ -0,0 +1,159 @@
/*
* 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 <sstream>
#include <arpa/inet.h>
#include <libiptc/libiptc.h>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <osquery/tables.h>
#include <osquery/filesystem.h>
#include <osquery/logger.h>
namespace osquery {
namespace tables {
const std::string kLinuxIpTablesNames = "/proc/net/ip_tables_names";
const char MAP[] = {'0','1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
const int HIGH_BITS = 4;
const int LOW_BITS = 15;
void parseIpEntry(ipt_ip *ip, Row &r) {
r["protocol"] = INTEGER(ip->proto);
if (strlen(ip->iniface)) {
r["iniface"] = TEXT(ip->iniface);
} else {
r["iniface"] = "all";
}
if (strlen(ip->outiface)) {
r["outiface"] = TEXT(ip->outiface);
} else {
r["outiface"] = "all";
}
char src_ip_string[INET6_ADDRSTRLEN] = {0};
if (inet_ntop(AF_INET, (struct in_addr *)&ip->src, src_ip_string, INET6_ADDRSTRLEN) != NULL) {
r["src_ip"] = TEXT(src_ip_string);
}
char dst_ip_string[INET6_ADDRSTRLEN] = {0};
if (inet_ntop(AF_INET, (struct in_addr *)&ip->dst, dst_ip_string, INET6_ADDRSTRLEN) != NULL) {
r["dst_ip"] = TEXT(dst_ip_string);
}
char src_ip_mask[INET6_ADDRSTRLEN] = {0};
if (inet_ntop(AF_INET, (struct in_addr *)&ip->smsk, src_ip_mask, INET6_ADDRSTRLEN) != NULL) {
r["src_mask"] = TEXT(src_ip_mask);
}
char dst_ip_mask[INET6_ADDRSTRLEN] = {0};
if (inet_ntop(AF_INET, (struct in_addr *)&ip->dmsk, dst_ip_mask, INET6_ADDRSTRLEN) != NULL) {
r["dst_mask"] = TEXT(dst_ip_mask);
}
char aux_char[2];
std::string iniface_mask = "";
for (int i = 0; ip->iniface_mask[i] != 0x00 && i<IFNAMSIZ; i++) {
aux_char[0] = MAP[(int) ip->iniface_mask[i] >> HIGH_BITS];
aux_char[1] = MAP[(int) ip->iniface_mask[i] & LOW_BITS];
iniface_mask += aux_char[0];
iniface_mask += aux_char[1];
}
r["iniface_mask"] = TEXT(iniface_mask);
std::string outiface_mask = "";
for (int i = 0; ip->outiface_mask[i] != 0x00 && i<IFNAMSIZ; i++) {
aux_char[0] = MAP[(int) ip->outiface_mask[i] >> HIGH_BITS];
aux_char[1] = MAP[(int) ip->outiface_mask[i] & LOW_BITS];
outiface_mask += aux_char[0];
outiface_mask += aux_char[1];
}
r["outiface_mask"] = TEXT(outiface_mask);
}
QueryData getIptablesRules(const std::string& content) {
QueryData results;
for (auto& line : split(content, "\n")) {
if (line.size() == 0) {
continue;
}
// Inline trim each line.
boost::trim(line);
Row r;
r["filter_name"] = TEXT(line);
// Initialize the access to iptc
auto handle = (struct iptc_handle*) iptc_init(line.c_str());
if (handle) {
// Iterate through chains
for (auto chain = iptc_first_chain((struct iptc_handle*)handle); chain; chain = iptc_next_chain((struct iptc_handle*)handle)) {
r["chain"] = TEXT(chain);
struct ipt_counters counters;
const char* policy;
if ((policy = iptc_get_policy(chain, &counters, (struct iptc_handle*)handle))) {
r["policy"] = TEXT(policy);
r["packets"] = INTEGER(counters.pcnt);
r["bytes"] = INTEGER(counters.bcnt);
}
struct ipt_entry *prev_rule;
// Iterating through all the rules per chain
for (auto chain_rule = iptc_first_rule(chain, (struct iptc_handle*)handle); chain_rule; chain_rule = iptc_next_rule(prev_rule, (struct iptc_handle*)handle)) {
prev_rule = (struct ipt_entry*)chain_rule;
auto target = iptc_get_target(chain_rule, (struct iptc_handle*)handle);
if (target) {
r["target"] = TEXT(target);
}
if (chain_rule->target_offset) {
r["match"] = "yes";
} else {
r["match"] = "no";
}
struct ipt_ip *ip = (struct ipt_ip*)&chain_rule->ip;
parseIpEntry(ip, r);
results.push_back(r);
} // Rule iteration
results.push_back(r);
} // Chain iteration
iptc_free((struct iptc_handle*) handle);
}
} // Filter table iteration
return results;
}
QueryData genIptables(QueryContext& context) {
std::string content;
QueryData results;
auto s = osquery::readFile(kLinuxIpTablesNames, content);
if (s.ok()) {
return getIptablesRules(content);
} else {
LOG(ERROR) << "Error reading " << kLinuxIpTablesNames << " : " << s.toString();
return {};
}
}
}
}

View File

@ -0,0 +1,66 @@
/*
* 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 <gtest/gtest.h>
#include <osquery/logger.h>
#include <osquery/database.h>
#include <libiptc/libiptc.h>
#include <arpa/inet.h>
#include "osquery/core/test_util.h"
namespace osquery {
namespace tables {
void parseIpEntry(ipt_ip *ip, Row &row);
ipt_ip* getIpEntryContent() {
static ipt_ip ip_entry;
ip_entry.proto = 6;
memset(ip_entry.iniface, 0, IFNAMSIZ);
strcpy(ip_entry.outiface, "eth0");
inet_aton("123.123.123.123", &ip_entry.src);
inet_aton("45.45.45.45", &ip_entry.dst);
inet_aton("250.251.252.253", &ip_entry.smsk);
inet_aton("253.252.251.250", &ip_entry.dmsk);
memset(ip_entry.iniface_mask, 0xfe, IFNAMSIZ );
memset(ip_entry.outiface_mask, 0xfa, IFNAMSIZ );
return &ip_entry;
}
Row getIpEntryExpectedResults() {
Row row;
row["protocol"] = "6";
row["iniface"] = "all";
row["outiface"] = "eth0";
row["src_ip"] = "123.123.123.123";
row["dst_ip"] = "45.45.45.45";
row["src_mask"] = "250.251.252.253";
row["dst_mask"] = "253.252.251.250";
row["iniface_mask"] = "FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE";
row["outiface_mask"] = "FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFA";
return row;
}
class IptablesTests : public testing::Test {};
TEST_F(IptablesTests, test_iptables_ip_entry) {
Row row;
parseIpEntry(getIpEntryContent(), row);
EXPECT_EQ(row, getIpEntryExpectedResults());
}
}
}

View File

@ -0,0 +1,21 @@
table_name("iptables")
description("Linux IP packet filtering and NAT tool.")
schema([
Column("filter_name", TEXT, "Packet matching filter table name."),
Column("chain", TEXT, "Size of module content."),
Column("policy", TEXT, "Policy that applies for this rule."),
Column("target", TEXT, "Target that applies for this rule."),
Column("protocol", INTEGER, "Protocol number identification."),
Column("src_ip", TEXT, "Source IP address."),
Column("src_mask", TEXT, "Source IP address mask."),
Column("iniface", TEXT, "Input interface for the rule."),
Column("iniface_mask", TEXT, "Input interface mask for the rule."),
Column("dst_ip", TEXT, "Destination IP address."),
Column("dst_mask", TEXT, "Destination IP address mask."),
Column("outiface", TEXT, "Output interface for the rule."),
Column("outiface_mask", TEXT, "Output interface mask for the rule."),
Column("match", TEXT, "Matching rule that applies."),
Column("packets", INTEGER, "Number of matching packets for this rule."),
Column("bytes", INTEGER, "Number of matching bytes for this rule."),
])
implementation("iptables@genIptables")

View File

@ -62,6 +62,9 @@ function main_centos() {
package rpm-devel
package rpm-build
package libblkid-devel
package iptables
package iptables-devel
patch_iptables_headers
install_cmake

View File

@ -151,6 +151,38 @@ function install_boost() {
fi
}
function patch_iptables_headers() {
IPV4FILE="/usr/include/linux/netfilter_ipv4/ip_tables.h"
IPV6FILE="/usr/include/linux/netfilter_ipv6/ip6_tables.h"
CODE_TO_PATCH="return (void \*)e + e->target_offset;"
if [[ -f "$IPV4FILE" ]]; then
if [[ -n `grep "$CODE_TO_PATCH" "$IPV4FILE"` ]]; then
log "IPv4 code to patch found, backing up first"
sudo cp "$IPV4FILE" "$IPV4FILE.osquery"
PATCH="return (struct ipt_entry_target *)((char *)e + e->target_offset);"
cat "$IPV4FILE" | sudo bash -c "sed \"s/$CODE_TO_PATCH/$PATCH/g\" > \"$IPV4FILE\""
log "IPv4 headers patched succesfully"
else
log "IPv4 code to patch not found, skipping."
fi
else
log "IPv4 iptables headers not found, skipping."
fi
if [[ -f "$IPV6FILE" ]]; then
if [[ -n `grep "$CODE_TO_PATCH" "$IPV6FILE"` ]]; then
log "IPv6 code to patch found, backing up first"
sudo cp "$IPV6FILE" "$IPV6FILE.osquery"
PATCH="return (struct ip6t_entry_target *)((char *)e + e->target_offset);"
cat "$IPV6FILE" | sudo bash -c "sed \"s/$CODE_TO_PATCH/$PATCH/g\" > \"$IPV6FILE\""
log "IPv6 headers patched succesfully"
else
log "IPv6 code to patch not found, skipping."
fi
else
log "IPv6 iptables headers not found, skipping."
fi
}
function install_gflags() {
if [[ ! -f /usr/local/lib/libgflags.a ]]; then
if [[ ! -f v2.1.1.tar.gz ]]; then

View File

@ -41,6 +41,9 @@ function main_rhel() {
package xz
package xz-devel
package subscription-manager
package iptables
package iptables-devel
patch_iptables_headers
if [[ -z `rpm -qa epel-release` ]]; then
if [[ $DISTRO = "rhel6" ]]; then

View File

@ -32,6 +32,9 @@ function main_ubuntu() {
package libbz2-dev
package devscripts
package debhelper
package iptables
package iptables-dev
patch_iptables_headers
if [[ $DISTRO = "precise" ]]; then
package clang-3.4