Skip to content

Commit

Permalink
Merge pull request #48 from vdesouza/main
Browse files Browse the repository at this point in the history
Adds `wifi_network` table to capture wifi network name.
  • Loading branch information
grahamgilbert authored May 25, 2024
2 parents 8325da2 + 4e7b680 commit 7054bfb
Show file tree
Hide file tree
Showing 6 changed files with 460 additions and 0 deletions.
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ go_library(
"//tables/puppet",
"//tables/sofa",
"//tables/unifiedlog",
"//tables/wifi_network",
"@com_github_osquery_osquery_go//:osquery-go",
"@com_github_osquery_osquery_go//plugin/table",
],
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ For production deployment, you should refer to the [osquery documentation](https
| `sofa_security_release_info` | The information on the security release the device is running from [Sofa](https://sofa.macadmins.io) | macOS | Use the `url` constraint to specify a data source other than `https://sofa.macadmins.io/v1/macos_data_feed.json` . By default this table will return vulnerability data for the running operating system. For historical data, use the `os_version` predicate (e.g `select * from sofa_security_release_info where os_version="14.4.0";`) |
| `sofa_unpatched_cves` | The CVEs that are unpatched on the device from [Sofa](https://sofa.macadmins.io) | macOS | Use the `url` constraint to specify a data source other than `https://sofa.macadmins.io/v1/macos_data_feed.json`. By default this table will return all unpatched vulnerability data. For historical data, use the `os_version` predicate (e.g `select * from sofa_unpatched_cves where os_version="14.4.0";`) |
| `unified_log` | Results from macOS' Unified Log | macOS | Use the constraints `predicate` and `last` to limit the number of results you pull, or this will not be very performant at all. Use `level` with a value of `info` to include info level messages. Use `level` with a value of `debug` to include info and debug level messages. (`select * from unified_log where last="1h" and level="debug" and predicate='processImagePath contains "mdmclient"';`) |
| `wifi_network` | Table to get the current wifi network name since the Osquery `wifi_info` table no longer does this. Includes the rest of the working fields in `wifi_info`. | macOS | See [osquery issue #8220](https://github.com/osquery/osquery/issues/8220) |

## Development

Expand Down
8 changes: 8 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/macadmins/osquery-extension/tables/puppet"
"github.com/macadmins/osquery-extension/tables/sofa"
"github.com/macadmins/osquery-extension/tables/unifiedlog"
"github.com/macadmins/osquery-extension/tables/wifi_network"

"github.com/macadmins/osquery-extension/tables/authdb"
osquery "github.com/osquery/osquery-go"
Expand Down Expand Up @@ -81,6 +82,13 @@ func main() {
return sofa.SofaUnpatchedCVEsGenerate(ctx, queryContext, *flSocketPath)
}),
table.NewPlugin("authdb", authdb.AuthDBColumns(), authdb.AuthDBGenerate),
table.NewPlugin(
"wifi_network",
wifi_network.WifiNetworkColumns(),
func(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
return wifi_network.WifiNetworkGenerate(ctx, queryContext, *flSocketPath)
},
),
}
plugins = append(plugins, darwinPlugins...)
}
Expand Down
23 changes: 23 additions & 0 deletions tables/wifi_network/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "wifi_network",
srcs = ["wifi_network.go"],
importpath = "github.com/macadmins/osquery-extension/tables/wifi_network",
visibility = ["//visibility:public"],
deps = [
"@com_github_osquery_osquery_go//:osquery-go",
"@com_github_osquery_osquery_go//plugin/table",
"@com_github_pkg_errors//:errors",
],
)

go_test(
name = "wifi_network_test",
srcs = ["wifi_network_test.go"],
embed = [":wifi_network"],
deps = [
"@com_github_osquery_osquery_go//plugin/table",
"@com_github_stretchr_testify//assert",
],
)
203 changes: 203 additions & 0 deletions tables/wifi_network/wifi_network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package wifi_network

import (
"context"
"os"
"os/exec"
"strings"
"time"

"github.com/osquery/osquery-go"
"github.com/osquery/osquery-go/plugin/table"
"github.com/pkg/errors"
)

type CommandExecutor interface {
ExecCommand(command string, args ...string) ([]byte, error)
}

type CmdExecutor struct{}

func (r CmdExecutor) ExecCommand(name string, args ...string) ([]byte, error) {
cmd := exec.Command(name, args...)
cmd.Stderr = os.Stderr
return cmd.Output()
}

type OsqueryClient interface {
QueryRow(query string) (map[string]string, error)
Close()
}

func WifiNetworkColumns() []table.ColumnDefinition {
return []table.ColumnDefinition{
table.TextColumn("ssid"),
table.TextColumn("interface"),
table.TextColumn("rssi"),
table.TextColumn("noise"),
table.TextColumn("channel"),
table.TextColumn("channel_width"),
table.TextColumn("channel_band"),
table.TextColumn("transmit_rate"),
table.TextColumn("security_type"),
table.TextColumn("mode"),
}
}

type WifiNetwork struct {
SSID string `json:"ssid"`
Interface string `json:"interface"`
RSSI string `json:"rssi"`
Noise string `json:"noise"`
Channel string `json:"channel"`
ChannelWidth string `json:"channel_width"`
ChannelBand string `json:"channel_band"`
TransmitRate string `json:"transmit_rate"`
SecurityType string `json:"security_type"`
Mode string `json:"mode"`
}

func WifiNetworkGenerate(
ctx context.Context,
queryContext table.QueryContext,
socketPath string,
) ([]map[string]string, error) {
// get the wifi interface from osquery
osqueryClient, err := osquery.NewClient(socketPath, 10*time.Second)
if err != nil {
return nil, err
}
defer osqueryClient.Close()

wifiStatus, err := getWifiStatus(osqueryClient)
if err != nil {
return nil, err
}

cmdExecutor := CmdExecutor{}
wifiNetwork, err := buildWifiNetworkFromResponse(cmdExecutor, wifiStatus)
if err != nil {
return nil, err
}

return buildWifiNetworkResults(wifiNetwork), nil
}

// getWifiInterface checks the wifi_status table to determine the wifi interface
func getWifiStatus(client OsqueryClient) (map[string]string, error) {
wifiStatusQuery := "SELECT * FROM wifi_status;"

resp, err := client.QueryRow(wifiStatusQuery)
if err != nil {
return nil, err
}
return resp, nil
}

// getValueFromResponse extracts the value from the response
func getValueFromResponse(resp map[string]string, key string) (string, error) {
if value, ok := resp[key]; ok {
return value, nil
}
return "", errors.New("wifi_status table missing '" + key + "' column")
}

// getWifiNetworkName shells out to 'networksetup -getairportnetwork ${wifiInterface}'
func getWifiNetworkName(cmdExecutor CommandExecutor, wifiInterface string) (string, error) {
out, err := cmdExecutor.ExecCommand("/usr/sbin/networksetup", "-getairportnetwork", wifiInterface)
if err != nil {
return "", errors.Wrap(err, "failed to run networksetup")
}
outStr := string(out)
splitOut := strings.Split(outStr, ": ")
if len(splitOut) != 2 {
// Wifi may not be on or connected
return "", nil
}
return strings.TrimSpace(splitOut[1]), nil
}

// buildWifiNetwork
func buildWifiNetworkFromResponse(cmdExecutor CommandExecutor, wifiStatus map[string]string) (*WifiNetwork, error) {
wifiInterface, err := getValueFromResponse(wifiStatus, "interface")
if err != nil {
return nil, err
}

rssi, err := getValueFromResponse(wifiStatus, "rssi")
if err != nil {
return nil, err
}

noise, err := getValueFromResponse(wifiStatus, "noise")
if err != nil {
return nil, err
}

channel, err := getValueFromResponse(wifiStatus, "channel")
if err != nil {
return nil, err
}

channelWidth, err := getValueFromResponse(wifiStatus, "channel_width")
if err != nil {
return nil, err
}

channelBand, err := getValueFromResponse(wifiStatus, "channel_band")
if err != nil {
return nil, err
}

transmitRate, err := getValueFromResponse(wifiStatus, "transmit_rate")
if err != nil {
return nil, err
}

securityType, err := getValueFromResponse(wifiStatus, "security_type")
if err != nil {
return nil, err
}

mode, err := getValueFromResponse(wifiStatus, "mode")
if err != nil {
return nil, err
}

// get the wifi network name
wifiNetworkName, err := getWifiNetworkName(cmdExecutor, wifiInterface)
if err != nil {
return nil, err
}

return &WifiNetwork{
SSID: wifiNetworkName,
Interface: wifiInterface,
RSSI: rssi,
Noise: noise,
Channel: channel,
ChannelWidth: channelWidth,
ChannelBand: channelBand,
TransmitRate: transmitRate,
SecurityType: securityType,
Mode: mode,
}, nil
}

// buildWifiNetworkResults creates a map of the results
func buildWifiNetworkResults(info *WifiNetwork) []map[string]string {
var results []map[string]string
results = append(results, map[string]string{
"ssid": info.SSID,
"interface": info.Interface,
"rssi": info.RSSI,
"noise": info.Noise,
"channel": info.Channel,
"channel_width": info.ChannelWidth,
"channel_band": info.ChannelBand,
"transmit_rate": info.TransmitRate,
"security_type": info.SecurityType,
"mode": info.Mode,
})
return results
}
Loading

0 comments on commit 7054bfb

Please sign in to comment.