diff --git a/osquery/core/conversions.h b/osquery/core/conversions.h index 48d7d252..e840b39d 100644 --- a/osquery/core/conversions.h +++ b/osquery/core/conversions.h @@ -169,6 +169,20 @@ inline Status safeStrtoll(const std::string& rep, size_t base, long long& out) { return Status(0); } +/// Safely convert a string representation of an integer base. +inline Status safeStrtoull(const std::string& rep, + size_t base, + unsigned long long& out) { + char* end{nullptr}; + out = strtoull(rep.c_str(), &end, static_cast(base)); + if (end == nullptr || end == rep.c_str() || *end != '\0' || + (out == ULLONG_MAX && errno == ERANGE)) { + out = 0; + return Status(1); + } + return Status(0); +} + /// Safely convert unicode escaped ASCII. inline std::string unescapeUnicode(const std::string& escaped) { if (escaped.size() < 6) { diff --git a/osquery/core/windows/wmi.cpp b/osquery/core/windows/wmi.cpp index c0142e99..0af5174f 100644 --- a/osquery/core/windows/wmi.cpp +++ b/osquery/core/windows/wmi.cpp @@ -113,7 +113,7 @@ Status WmiResultItem::GetUnsignedShort(const std::string& name, if (hr != S_OK) { return Status(-1, "Error retrieving data from WMI query."); } - if (value.vt != VT_I4) { + if (value.vt != VT_UI2) { VariantClear(&value); return Status(-1, "Invalid data type returned."); } @@ -131,7 +131,7 @@ Status WmiResultItem::GetUnsignedInt32(const std::string& name, if (hr != S_OK) { return Status(-1, "Error retrieving data from WMI query."); } - if (value.vt != VT_I4) { + if (value.vt != VT_UINT) { VariantClear(&value); return Status(-1, "Invalid data type returned."); } diff --git a/osquery/tables/networking/tests/networking_tables_tests.cpp b/osquery/tables/networking/tests/networking_tables_tests.cpp index edcacbe8..4819cad5 100644 --- a/osquery/tables/networking/tests/networking_tables_tests.cpp +++ b/osquery/tables/networking/tests/networking_tables_tests.cpp @@ -51,5 +51,16 @@ TEST_F(NetworkingTablesTests, test_listening_ports) { EXPECT_NE(pid, "-1"); server.stop(); } + +TEST_F(NetworkingTablesTests, test_address_details_join) { + // Expect that we can join interface addresses with details + auto query = + "select * from interface_details id, interface_addresses ia " + "on ia.interface = id.interface " + "where ia.address = '127.0.0.1';"; + + auto results = SQL(query); + EXPECT_GT(results.rows().size(), 0U); +} } } diff --git a/osquery/tables/networking/windows/arp_cache.cpp b/osquery/tables/networking/windows/arp_cache.cpp index d4ee5663..90848ddc 100644 --- a/osquery/tables/networking/windows/arp_cache.cpp +++ b/osquery/tables/networking/windows/arp_cache.cpp @@ -19,7 +19,7 @@ namespace osquery { namespace tables { -const std::map kMapOfAddressFamily = { +const std::map kMapOfAddressFamily = { {2, "IPv4"}, {23, "IPv6"}, }; @@ -47,10 +47,6 @@ QueryData genIPv4ArpCache(QueryContext& context) { std::map mapOfInterfaces = { {1, ""}, // loopback }; - unsigned short usiPlaceHolder; - unsigned char cPlaceHolder; - unsigned int uiPlaceHolder; - std::string strPlaceHolder; for (const auto& iface : interfaces) { long interfaceIndex; @@ -62,11 +58,14 @@ QueryData genIPv4ArpCache(QueryContext& context) { } } + long lPlaceHolder = 0; + unsigned char cPlaceHolder; + std::string strPlaceHolder; for (const auto& item : wmiResults) { Row r; - item.GetUnsignedShort("AddressFamily", usiPlaceHolder); - r["address_family"] = kMapOfAddressFamily.count(usiPlaceHolder) > 0 - ? kMapOfAddressFamily.at(usiPlaceHolder) + item.GetLong("AddressFamily", lPlaceHolder); + r["address_family"] = kMapOfAddressFamily.count(lPlaceHolder) > 0 + ? kMapOfAddressFamily.at(lPlaceHolder) : "-1"; item.GetUChar("Store", cPlaceHolder); r["store"] = kMapOfStore.count(cPlaceHolder) > 0 @@ -76,9 +75,9 @@ QueryData genIPv4ArpCache(QueryContext& context) { r["state"] = kMapOfState.count(cPlaceHolder) > 0 ? kMapOfState.at(cPlaceHolder) : "-1"; - item.GetUnsignedInt32("InterfaceIndex", uiPlaceHolder); - r["interface"] = mapOfInterfaces.count(uiPlaceHolder) > 0 - ? mapOfInterfaces.at(uiPlaceHolder) + item.GetLong("InterfaceIndex", lPlaceHolder); + r["interface"] = mapOfInterfaces.count(lPlaceHolder) > 0 + ? mapOfInterfaces.at(lPlaceHolder) : "-1"; item.GetString("IPAddress", r["ip_address"]); item.GetString("InterfaceAlias", r["interface_alias"]); diff --git a/osquery/tables/networking/windows/interfaces.cpp b/osquery/tables/networking/windows/interfaces.cpp index 4a65a8e1..175a4619 100644 --- a/osquery/tables/networking/windows/interfaces.cpp +++ b/osquery/tables/networking/windows/interfaces.cpp @@ -19,88 +19,192 @@ #include #include +#include #include +#include "osquery/core/conversions.h" #include "osquery/core/windows/wmi.h" namespace osquery { namespace tables { -auto kMaxBufferAllocRetries = 3; -auto kWorkingBufferSize = 15000; +const auto kMaxBufferAllocRetries = 3; +const auto kWorkingBufferSize = 15000; -void genInterfaceDetail(const WmiResultItem& adapter, QueryData& results) { - Row r; - long lPlaceHolder; - bool bPlaceHolder; - std::vector vPlaceHolder; - unsigned __int64 ulPlaceHolder; +Status genInterfaceDetail(const IP_ADAPTER_ADDRESSES* adapter, Row& r) { + r["interface"] = INTEGER(adapter->IfIndex); + r["mtu"] = INTEGER(adapter->Mtu); + r["type"] = INTEGER(adapter->IfType); + r["description"] = wstringToString(adapter->Description); - adapter.GetString("MACAddress", r["mac"]); - adapter.GetString("AdapterType", r["type"]); - adapter.GetString("Description", r["description"]); - adapter.GetLong("InterfaceIndex", lPlaceHolder); - r["interface"] = SQL_TEXT(lPlaceHolder); - adapter.GetString("Manufacturer", r["manufacturer"]); - adapter.GetString("NetConnectionID", r["connection_id"]); - adapter.GetLong("NetConnectionStatus", lPlaceHolder); - r["connection_status"] = INTEGER(lPlaceHolder); - adapter.GetBool("NetEnabled", bPlaceHolder); - r["enabled"] = INTEGER(bPlaceHolder); - adapter.GetBool("PhysicalAdapter", bPlaceHolder); - r["physical_adapter"] = INTEGER(bPlaceHolder); - adapter.GetUnsignedLongLong("Speed", ulPlaceHolder); - r["speed"] = INTEGER(ulPlaceHolder); + std::vector toks; + for (size_t i = 0; i < adapter->PhysicalAddressLength; i++) { + std::stringstream ss; + ss << std::hex; + ss << static_cast(adapter->PhysicalAddress[i]); + auto s = ss.str(); + if (s.size() < 2_sz) { + s = '0' + s; + } + toks.push_back(s); + } + r["mac"] = osquery::join(toks, ":"); + r["flags"] = INTEGER(adapter->Flags); + r["metric"] = INTEGER(adapter->Ipv4Metric); + // TODO: These values will need an equivalent on Windows systems + r["last_change"] = BIGINT("-1"); + r["collisions"] = BIGINT("-1"); + + // Grab the remaining table values from WMI + Status s; auto query = + "SELECT * FROM Win32_PerfRawData_Tcpip_NetworkInterface WHERE " + "Name = \"" + + r["description"] + "\""; + WmiRequest req1(query); + if (req1.getStatus().ok()) { + auto& results = req1.results(); + if (!results.empty()) { + std::string sPlaceHolder; + unsigned long long ullPlaceHolder = 0; + + results[0].GetString("PacketsReceivedPerSec", sPlaceHolder); + safeStrtoull(sPlaceHolder, 10, ullPlaceHolder); + r["ipackets"] = BIGINT(ullPlaceHolder); + results[0].GetString("PacketsSentPerSec", sPlaceHolder); + safeStrtoull(sPlaceHolder, 10, ullPlaceHolder); + r["opackets"] = BIGINT(ullPlaceHolder); + + results[0].GetString("BytesReceivedPerSec", sPlaceHolder); + safeStrtoull(sPlaceHolder, 10, ullPlaceHolder); + r["ibytes"] = BIGINT(ullPlaceHolder); + results[0].GetString("BytesSentPerSec", sPlaceHolder); + safeStrtoull(sPlaceHolder, 10, ullPlaceHolder); + r["obytes"] = BIGINT(ullPlaceHolder); + + results[0].GetString("PacketsReceivedErrors", sPlaceHolder); + safeStrtoull(sPlaceHolder, 10, ullPlaceHolder); + r["ierrors"] = BIGINT(ullPlaceHolder); + results[0].GetString("PacketsOutboundErrors", sPlaceHolder); + safeStrtoull(sPlaceHolder, 10, ullPlaceHolder); + r["oerrors"] = BIGINT(ullPlaceHolder); + + results[0].GetString("PacketsReceivedDiscarded", sPlaceHolder); + safeStrtoull(sPlaceHolder, 10, ullPlaceHolder); + r["idrops"] = BIGINT(ullPlaceHolder); + results[0].GetString("PacketsOutboundDiscarded", sPlaceHolder); + safeStrtoull(sPlaceHolder, 10, ullPlaceHolder); + r["odrops"] = BIGINT(ullPlaceHolder); + } else { + r["ipackets"] = BIGINT("-1"); + r["opackets"] = BIGINT("-1"); + r["ibytes"] = BIGINT("-1"); + r["obytes"] = BIGINT("-1"); + r["ierrors"] = BIGINT("-1"); + r["oerrors"] = BIGINT("-1"); + r["idrops"] = BIGINT("-1"); + r["odrops"] = BIGINT("-1"); + s = Status(1, "Failed to enumerate extended interface details"); + } + } + + query = + "SELECT * FROM Win32_NetworkAdapter WHERE " + "InterfaceIndex = " + + r["interface"]; + WmiRequest req2(query); + if (req2.getStatus().ok()) { + auto& results = req2.results(); + if (!results.empty()) { + bool bPlaceHolder; + long lPlaceHolder = 0; + unsigned __int64 ullPlaceHolder = 0; + results[0].GetString("NetConnectionID", r["connection_id"]); + results[0].GetLong("NetConnectionStatus", lPlaceHolder); + r["connection_status"] = INTEGER(lPlaceHolder); + results[0].GetBool("NetEnabled", bPlaceHolder); + r["enabled"] = INTEGER(bPlaceHolder); + results[0].GetBool("PhysicalAdapter", bPlaceHolder); + r["physical_adapter"] = INTEGER(bPlaceHolder); + results[0].GetUnsignedLongLong("Speed", ullPlaceHolder); + r["speed"] = INTEGER(ullPlaceHolder); + } else { + s = Status(1, "Failed to enumerate extended interface details"); + } + } + + query = "SELECT * FROM win32_networkadapterconfiguration WHERE " "InterfaceIndex = " + r["interface"]; - - WmiRequest irequest(query); - if (irequest.getStatus().ok()) { - auto& iresults = irequest.results(); - if (iresults.empty()) { - return; + WmiRequest req3(query); + if (req3.getStatus().ok()) { + auto& results = req3.results(); + if (!results.empty()) { + bool bPlaceHolder; + std::vector vPlaceHolder; + results[0].GetBool("DHCPEnabled", bPlaceHolder); + r["dhcp_enabled"] = INTEGER(bPlaceHolder); + results[0].GetString("DHCPLeaseExpires", r["dhcp_lease_expires"]); + results[0].GetString("DHCPLeaseObtained", r["dhcp_lease_obtained"]); + results[0].GetString("DHCPServer", r["dhcp_server"]); + results[0].GetString("DNSDomain", r["dns_domain"]); + results[0].GetVectorOfStrings("DNSDomainSuffixSearchOrder", vPlaceHolder); + r["dns_domain_suffix_search_order"] = osquery::join(vPlaceHolder, ", "); + results[0].GetString("DNSHostName", r["dns_host_name"]); + results[0].GetVectorOfStrings("DNSServerSearchOrder", vPlaceHolder); + r["dns_server_search_order"] = osquery::join(vPlaceHolder, ", "); + } else { + s = Status(1, "Failed to enumerate extended interface details"); } - iresults[0].GetLong("MTU", lPlaceHolder); - r["mtu"] = INTEGER(lPlaceHolder); - - iresults[0].GetBool("DHCPEnabled", bPlaceHolder); - r["dhcp_enabled"] = INTEGER(bPlaceHolder); - iresults[0].GetString("DHCPLeaseExpires", r["dhcp_lease_expires"]); - iresults[0].GetString("DHCPLeaseObtained", r["dhcp_lease_obtained"]); - iresults[0].GetString("DHCPServer", r["dhcp_server"]); - iresults[0].GetString("DNSDomain", r["dns_domain"]); - iresults[0].GetVectorOfStrings("DNSDomainSuffixSearchOrder", vPlaceHolder); - r["dns_domain_suffix_search_order"] = - SQL_TEXT(boost::algorithm::join(vPlaceHolder, ", ")); - iresults[0].GetString("DNSHostName", r["dns_host_name"]); - iresults[0].GetVectorOfStrings("DNSServerSearchOrder", vPlaceHolder); - r["dns_server_search_order"] = - SQL_TEXT(boost::algorithm::join(vPlaceHolder, ", ")); } - results.push_back(r); + return s; } QueryData genInterfaceDetails(QueryContext& context) { QueryData results; - WmiRequest request("SELECT * FROM Win32_NetworkAdapter"); - if (request.getStatus().ok()) { - auto& wmi_results = request.results(); - for (const auto& adapter : wmi_results) { - genInterfaceDetail(adapter, results); + + DWORD buffSize = kWorkingBufferSize; + auto alloc_attempts = 0; + size_t alloc_result = 0; + const auto addrFamily = AF_UNSPEC; + const auto addrFlags = + GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST; + std::unique_ptr adapters(nullptr); + + // Buffer size can change between the query and malloc (if adapters are + // added/removed), so shenanigans are required + do { + adapters.reset(static_cast(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; + } + + const IP_ADAPTER_ADDRESSES* currAdapter = adapters.get(); + while (currAdapter != nullptr) { + Row r; + auto s = genInterfaceDetail(currAdapter, r); + if (!s.ok()) { + // The only failure we might expect is the extended details enumeration + // in which we do not care to WARN + VLOG(1) << s.getMessage(); + } + currAdapter = currAdapter->Next; + results.push_back(r); } return results; } -void genInterfaceAddress(const std::string& name, - const IP_ADAPTER_UNICAST_ADDRESS* ipaddr, - QueryData& results) { - Row r; - r["interface"] = name; - +void genInterfaceAddress(const IP_ADAPTER_UNICAST_ADDRESS* ipaddr, Row& r) { switch (ipaddr->SuffixOrigin) { case IpSuffixOriginManual: r["type"] = "manual"; @@ -150,20 +254,18 @@ void genInterfaceAddress(const std::string& name, INET6_ADDRSTRLEN); r["address"] = addrBuff; } - results.emplace_back(r); } QueryData genInterfaceAddresses(QueryContext& context) { QueryData results; + DWORD buffSize = kWorkingBufferSize; auto alloc_attempts = 0; - size_t alloc_result; + size_t alloc_result = 0; 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 adapters(nullptr, - freeMem); + std::unique_ptr adapters(nullptr); // Buffer size can change between the query and malloc (if adapters are // added/removed), so shenanigans are required @@ -188,8 +290,12 @@ QueryData genInterfaceAddresses(QueryContext& context) { const IP_ADAPTER_UNICAST_ADDRESS* ipaddr = currAdapter->FirstUnicastAddress; while (ipaddr != nullptr) { - genInterfaceAddress(adapterName, ipaddr, results); + Row r; + r["interface"] = SQL_TEXT(currAdapter->IfIndex); + r["friendly_name"] = adapterName; + genInterfaceAddress(ipaddr, r); ipaddr = ipaddr->Next; + results.push_back(r); } currAdapter = currAdapter->Next; } diff --git a/specs/interface_addresses.table b/specs/interface_addresses.table index 0598c88a..1349412f 100644 --- a/specs/interface_addresses.table +++ b/specs/interface_addresses.table @@ -8,5 +8,8 @@ schema([ Column("point_to_point", TEXT, "PtP address for the interface"), Column("type", TEXT, "Type of address. One of dhcp, manual, auto, other") ]) +extended_schema(WINDOWS, [ + Column("friendly_name", TEXT, "The friendly display name of the interface."), +]) attributes(cacheable=True) implementation("interfaces@genInterfaceAddresses") diff --git a/specs/interface_details.table b/specs/interface_details.table index dce66edf..b97011c4 100644 --- a/specs/interface_details.table +++ b/specs/interface_details.table @@ -19,6 +19,7 @@ schema([ Column("last_change", BIGINT, "Time of last device modification (optional)"), ]) extended_schema(WINDOWS, [ + Column("friendly_name", TEXT, "The friendly display name of the interface."), Column("description", TEXT, "Short description of the object—a one-line string."), Column("manufacturer", TEXT, "Name of the network adapter's manufacturer."), Column("connection_id", TEXT, "Name of the network connection as it appears in the Network Connections Control Panel program."),