Refactor Windows interface_addresses table to use win32 API and report accurate netmasks. (#3297)

This commit is contained in:
ryanheffernan 2017-05-23 14:58:11 -07:00 committed by GitHub
parent e9dc7b0aa5
commit 05b7b80891
3 changed files with 109 additions and 30 deletions

View File

@ -10,6 +10,12 @@
#include <string>
// clang-format off
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <iphlpapi.h>
// clang-format on
#include <boost/algorithm/string/join.hpp>
#include <osquery/core.h>
@ -21,6 +27,9 @@
namespace osquery {
namespace tables {
auto kMaxBufferAllocRetries = 3;
auto kWorkingBufferSize = 15000;
QueryData genInterfaceDetails(QueryContext& context) {
QueryData results_data;
WmiRequest request("SELECT * FROM Win32_NetworkAdapter");
@ -80,32 +89,98 @@ QueryData genInterfaceDetails(QueryContext& context) {
}
QueryData genInterfaceAddresses(QueryContext& context) {
QueryData results_data;
WmiRequest request(
"SELECT * FROM win32_networkadapterconfiguration where IPEnabled=TRUE");
if (request.getStatus().ok()) {
std::vector<WmiResultItem>& results = request.results();
for (const auto& result : results) {
Row r;
long lPlaceHolder;
std::vector<std::string> vPlaceHolderIps;
std::vector<std::string> vPlaceHolderSubnets;
QueryData results;
DWORD buffSize = kWorkingBufferSize;
auto alloc_attempts = 0;
size_t alloc_result;
const auto addrFamily = AF_UNSPEC;
const auto addrFlags =
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST;
const auto freeMem = [](auto ptr) { free(ptr); };
std::unique_ptr<IP_ADAPTER_ADDRESSES, decltype(freeMem)> adapters(nullptr,
freeMem);
result.GetLong("InterfaceIndex", lPlaceHolder);
r["interface"] = SQL_TEXT(lPlaceHolder);
result.GetVectorOfStrings("IPAddress", vPlaceHolderIps);
result.GetVectorOfStrings("IPSubnet", vPlaceHolderSubnets);
for (size_t i = 0; i < vPlaceHolderIps.size(); i++) {
r["address"] = SQL_TEXT(vPlaceHolderIps.at(i));
if (vPlaceHolderSubnets.size() > i) {
r["mask"] = SQL_TEXT(vPlaceHolderSubnets.at(i));
}
results_data.push_back(r);
}
// Buffer size can change between the query and malloc (if adapters are
// added/removed), so shenanigans are required
do {
adapters.reset(static_cast<PIP_ADAPTER_ADDRESSES>(malloc(buffSize)));
if (adapters == nullptr) {
return results;
}
alloc_result = GetAdaptersAddresses(
addrFamily, addrFlags, nullptr, adapters.get(), &buffSize);
alloc_attempts++;
} while (alloc_result == ERROR_BUFFER_OVERFLOW &&
alloc_attempts < kMaxBufferAllocRetries);
if (alloc_result != NO_ERROR) {
return results;
}
return results_data;
}
}
const IP_ADAPTER_ADDRESSES* currAdapter = adapters.get();
while (currAdapter != nullptr) {
std::wstring wsAdapterName = std::wstring(currAdapter->FriendlyName);
std::string adapterName =
std::string(wsAdapterName.begin(), wsAdapterName.end());
const IP_ADAPTER_UNICAST_ADDRESS* ipaddr = currAdapter->FirstUnicastAddress;
while (ipaddr != nullptr) {
Row r;
r["interface"] = adapterName;
switch (ipaddr->SuffixOrigin) {
case IpSuffixOriginManual:
r["type"] = "manual";
break;
case IpSuffixOriginDhcp:
r["type"] = "dhcp";
break;
case IpSuffixOriginLinkLayerAddress:
case IpSuffixOriginRandom:
r["type"] = "auto";
break;
default:
r["type"] = "unknown";
}
if (ipaddr->Address.lpSockaddr->sa_family == AF_INET) {
ULONG mask;
ConvertLengthToIpv4Mask(ipaddr->OnLinkPrefixLength, &mask);
in_addr maskAddr;
maskAddr.s_addr = mask;
char addrBuff[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, &maskAddr, addrBuff, INET_ADDRSTRLEN);
r["mask"] = addrBuff;
inet_ntop(AF_INET,
&reinterpret_cast<sockaddr_in*>(ipaddr->Address.lpSockaddr)
->sin_addr,
addrBuff,
INET_ADDRSTRLEN);
r["address"] = addrBuff;
} else if (ipaddr->Address.lpSockaddr->sa_family == AF_INET6) {
in6_addr netmask;
memset(&netmask, 0x0, sizeof(netmask));
for (long i = ipaddr->OnLinkPrefixLength, j = 0; i > 0; i -= 8, ++j)
netmask.s6_addr[j] = i >= 8 ? 0xff : (ULONG)((0xffU << (8 - i)));
char addrBuff[INET6_ADDRSTRLEN] = {0};
inet_ntop(AF_INET6, &netmask, addrBuff, INET6_ADDRSTRLEN);
r["mask"] = addrBuff;
inet_ntop(AF_INET6,
&reinterpret_cast<sockaddr_in6*>(ipaddr->Address.lpSockaddr)
->sin6_addr,
addrBuff,
INET6_ADDRSTRLEN);
r["address"] = addrBuff;
}
results.emplace_back(r);
ipaddr = ipaddr->Next;
}
currAdapter = currAdapter->Next;
}
return results;
}
} // namespace tables
} // namespace osquery

View File

@ -45,15 +45,13 @@ const std::map<int, std::string> kServiceType = {
auto closeServiceHandle = [](SC_HANDLE sch) { CloseServiceHandle(sch); };
using svc_handle_t = std::unique_ptr<SC_HANDLE__, decltype(closeServiceHandle)>;
auto freeMem = [](auto ptr) { free(ptr); };
using svc_descr_t = std::unique_ptr<SERVICE_DESCRIPTION, decltype(freeMem)>;
using svc_query_t = std::unique_ptr<QUERY_SERVICE_CONFIG, decltype(freeMem)>;
using enum_svc_status_t =
std::unique_ptr<ENUM_SERVICE_STATUS_PROCESS[], decltype(freeMem)>;
static inline Status getService(const SC_HANDLE& scmHandle,
const ENUM_SERVICE_STATUS_PROCESS& svc,
QueryData& results) {
auto freeMem = [](auto ptr) { free(ptr); };
using svc_descr_t = std::unique_ptr<SERVICE_DESCRIPTION, decltype(freeMem)>;
using svc_query_t = std::unique_ptr<QUERY_SERVICE_CONFIG, decltype(freeMem)>;
Row r;
svc_handle_t svcHandle(
OpenService(scmHandle, svc.lpServiceName, SERVICE_QUERY_CONFIG),
@ -139,6 +137,11 @@ static inline Status getService(const SC_HANDLE& scmHandle,
}
static inline Status getServices(QueryData& results) {
auto freeMem = [](auto ptr) { free(ptr); };
using svc_descr_t = std::unique_ptr<SERVICE_DESCRIPTION, decltype(freeMem)>;
using enum_svc_status_t =
std::unique_ptr<ENUM_SERVICE_STATUS_PROCESS[], decltype(freeMem)>;
svc_handle_t scmHandle(OpenSCManager(nullptr, nullptr, GENERIC_READ),
closeServiceHandle);
if (scmHandle == nullptr) {

View File

@ -6,6 +6,7 @@ schema([
Column("mask", TEXT, "Interface netmask"),
Column("broadcast", TEXT, "Broadcast address for the interface"),
Column("point_to_point", TEXT, "PtP address for the interface"),
Column("type", TEXT, "Type of address. One of dhcp, manual, auto, other")
])
attributes(cacheable=True)
implementation("interfaces@genInterfaceAddresses")