wpasupplicant: Disable D-Bus auto-activation on all calls
Every method call and property access to fi.w1.wpa_supplicant1 used flag 0, leaving D-Bus service activation enabled. Any stray request — a startup race before `service wpa_supplicant start` takes effect, or a leaked call while the Ethernet uplink is the chosen path — would make the bus daemon spawn `wpa_supplicant -u` from its .service file, bypassing repeater's own launch of the daemon. Route every interaction through callNoAutoStart/getPropNoAutoStart/ setPropNoAutoStart, which set dbus.FlagNoAutoStart. A leaked call now fails cleanly with ServiceUnknown instead of resurrecting the daemon, leaving repeater as the sole launcher. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
15d17ed35e
commit
11ae69de56
5 changed files with 85 additions and 25 deletions
|
|
@ -54,7 +54,7 @@ func (b *WPABackend) getInterfacePath(interfaceName string) (dbus.ObjectPath, er
|
||||||
var interfacePath dbus.ObjectPath
|
var interfacePath dbus.ObjectPath
|
||||||
|
|
||||||
// Try to get existing interface
|
// Try to get existing interface
|
||||||
err := b.wpasupplicant.Call(Service+".GetInterface", 0, interfaceName).Store(&interfacePath)
|
err := callNoAutoStart(b.wpasupplicant, Service+".GetInterface", interfaceName).Store(&interfacePath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return interfacePath, nil
|
return interfacePath, nil
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ func (b *WPABackend) getInterfacePath(interfaceName string) (dbus.ObjectPath, er
|
||||||
"Ifname": dbus.MakeVariant(interfaceName),
|
"Ifname": dbus.MakeVariant(interfaceName),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.wpasupplicant.Call(Service+".CreateInterface", 0, args).Store(&interfacePath)
|
err = callNoAutoStart(b.wpasupplicant, Service+".CreateInterface", args).Store(&interfacePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to create interface: %v", err)
|
return "", fmt.Errorf("failed to create interface: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,49 +38,49 @@ func (b *BSS) GetProperties() (*BSSProperties, error) {
|
||||||
props := &BSSProperties{}
|
props := &BSSProperties{}
|
||||||
|
|
||||||
// Get SSID
|
// Get SSID
|
||||||
if ssidProp, err := b.obj.GetProperty(BSSInterface + ".SSID"); err == nil {
|
if ssidProp, err := getPropNoAutoStart(b.obj, BSSInterface+".SSID"); err == nil {
|
||||||
if ssid, ok := ssidProp.Value().([]byte); ok {
|
if ssid, ok := ssidProp.Value().([]byte); ok {
|
||||||
props.SSID = ssid
|
props.SSID = ssid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get BSSID
|
// Get BSSID
|
||||||
if bssidProp, err := b.obj.GetProperty(BSSInterface + ".BSSID"); err == nil {
|
if bssidProp, err := getPropNoAutoStart(b.obj, BSSInterface+".BSSID"); err == nil {
|
||||||
if bssid, ok := bssidProp.Value().([]byte); ok {
|
if bssid, ok := bssidProp.Value().([]byte); ok {
|
||||||
props.BSSID = bssid
|
props.BSSID = bssid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Signal
|
// Get Signal
|
||||||
if signalProp, err := b.obj.GetProperty(BSSInterface + ".Signal"); err == nil {
|
if signalProp, err := getPropNoAutoStart(b.obj, BSSInterface+".Signal"); err == nil {
|
||||||
if signal, ok := signalProp.Value().(int16); ok {
|
if signal, ok := signalProp.Value().(int16); ok {
|
||||||
props.Signal = signal
|
props.Signal = signal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Frequency
|
// Get Frequency
|
||||||
if freqProp, err := b.obj.GetProperty(BSSInterface + ".Frequency"); err == nil {
|
if freqProp, err := getPropNoAutoStart(b.obj, BSSInterface+".Frequency"); err == nil {
|
||||||
if freq, ok := freqProp.Value().(uint16); ok {
|
if freq, ok := freqProp.Value().(uint16); ok {
|
||||||
props.Frequency = uint32(freq)
|
props.Frequency = uint32(freq)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Privacy
|
// Get Privacy
|
||||||
if privacyProp, err := b.obj.GetProperty(BSSInterface + ".Privacy"); err == nil {
|
if privacyProp, err := getPropNoAutoStart(b.obj, BSSInterface+".Privacy"); err == nil {
|
||||||
if privacy, ok := privacyProp.Value().(bool); ok {
|
if privacy, ok := privacyProp.Value().(bool); ok {
|
||||||
props.Privacy = privacy
|
props.Privacy = privacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get RSN (WPA2) information
|
// Get RSN (WPA2) information
|
||||||
if rsnProp, err := b.obj.GetProperty(BSSInterface + ".RSN"); err == nil {
|
if rsnProp, err := getPropNoAutoStart(b.obj, BSSInterface+".RSN"); err == nil {
|
||||||
if rsn, ok := rsnProp.Value().(map[string]dbus.Variant); ok {
|
if rsn, ok := rsnProp.Value().(map[string]dbus.Variant); ok {
|
||||||
props.RSN = rsn
|
props.RSN = rsn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get WPA information
|
// Get WPA information
|
||||||
if wpaProp, err := b.obj.GetProperty(BSSInterface + ".WPA"); err == nil {
|
if wpaProp, err := getPropNoAutoStart(b.obj, BSSInterface+".WPA"); err == nil {
|
||||||
if wpa, ok := wpaProp.Value().(map[string]dbus.Variant); ok {
|
if wpa, ok := wpaProp.Value().(map[string]dbus.Variant); ok {
|
||||||
props.WPA = wpa
|
props.WPA = wpa
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +91,7 @@ func (b *BSS) GetProperties() (*BSSProperties, error) {
|
||||||
|
|
||||||
// GetSSIDString returns the SSID as a string
|
// GetSSIDString returns the SSID as a string
|
||||||
func (b *BSS) GetSSIDString() (string, error) {
|
func (b *BSS) GetSSIDString() (string, error) {
|
||||||
prop, err := b.obj.GetProperty(BSSInterface + ".SSID")
|
prop, err := getPropNoAutoStart(b.obj, BSSInterface+".SSID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get SSID property: %v", err)
|
return "", fmt.Errorf("failed to get SSID property: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +106,7 @@ func (b *BSS) GetSSIDString() (string, error) {
|
||||||
|
|
||||||
// GetBSSIDString returns the BSSID as a formatted MAC address string
|
// GetBSSIDString returns the BSSID as a formatted MAC address string
|
||||||
func (b *BSS) GetBSSIDString() (string, error) {
|
func (b *BSS) GetBSSIDString() (string, error) {
|
||||||
prop, err := b.obj.GetProperty(BSSInterface + ".BSSID")
|
prop, err := getPropNoAutoStart(b.obj, BSSInterface+".BSSID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get BSSID property: %v", err)
|
return "", fmt.Errorf("failed to get BSSID property: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +122,7 @@ func (b *BSS) GetBSSIDString() (string, error) {
|
||||||
|
|
||||||
// GetSignal returns the signal strength in dBm
|
// GetSignal returns the signal strength in dBm
|
||||||
func (b *BSS) GetSignal() (int16, error) {
|
func (b *BSS) GetSignal() (int16, error) {
|
||||||
prop, err := b.obj.GetProperty(BSSInterface + ".Signal")
|
prop, err := getPropNoAutoStart(b.obj, BSSInterface+".Signal")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to get Signal property: %v", err)
|
return 0, fmt.Errorf("failed to get Signal property: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
60
internal/wifi/wpasupplicant/dbusutil.go
Normal file
60
internal/wifi/wpasupplicant/dbusutil.go
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
package wpasupplicant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// D-Bus interactions with the fi.w1.wpa_supplicant1 name must never trigger
|
||||||
|
// service activation. repeater is the sole launcher of the daemon (via
|
||||||
|
// `service wpa_supplicant start` in the app layer); if a call reached the bus
|
||||||
|
// name while the daemon was down — a startup race, or a stray call in
|
||||||
|
// Ethernet-uplink mode — the bus would auto-activate it from its .service
|
||||||
|
// file, spawning `wpa_supplicant -u` and bypassing repeater's own launch.
|
||||||
|
//
|
||||||
|
// godbus only suppresses activation when FlagNoAutoStart is set on the message
|
||||||
|
// (msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)); the default
|
||||||
|
// flag value of 0 leaves activation enabled. The helpers below set the flag on
|
||||||
|
// every method call and property access. A leaked call then fails cleanly with
|
||||||
|
// a ServiceUnknown error instead of resurrecting the daemon.
|
||||||
|
|
||||||
|
// callNoAutoStart performs a method call with D-Bus auto-activation disabled.
|
||||||
|
func callNoAutoStart(obj dbus.BusObject, method string, args ...interface{}) *dbus.Call {
|
||||||
|
return obj.Call(method, dbus.FlagNoAutoStart, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPropNoAutoStart reads a property (interface.member notation) with
|
||||||
|
// auto-activation disabled. It mirrors dbus.Object.GetProperty, which
|
||||||
|
// otherwise issues Properties.Get with the activation-enabling flag 0.
|
||||||
|
func getPropNoAutoStart(obj dbus.BusObject, p string) (dbus.Variant, error) {
|
||||||
|
var result dbus.Variant
|
||||||
|
iface, prop, err := splitProperty(p)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
err = obj.Call("org.freedesktop.DBus.Properties.Get", dbus.FlagNoAutoStart, iface, prop).Store(&result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPropNoAutoStart writes a property with auto-activation disabled. The value
|
||||||
|
// is passed through as-is, so callers wrap it in a dbus.Variant just as they
|
||||||
|
// would for dbus.Object.SetProperty.
|
||||||
|
func setPropNoAutoStart(obj dbus.BusObject, p string, v interface{}) error {
|
||||||
|
iface, prop, err := splitProperty(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return obj.Call("org.freedesktop.DBus.Properties.Set", dbus.FlagNoAutoStart, iface, prop, v).Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitProperty splits an "interface.member" property name into its interface
|
||||||
|
// and member parts, matching godbus's own parsing.
|
||||||
|
func splitProperty(p string) (iface, prop string, err error) {
|
||||||
|
idx := strings.LastIndex(p, ".")
|
||||||
|
if idx == -1 || idx+1 == len(p) {
|
||||||
|
return "", "", fmt.Errorf("dbus: invalid property %s", p)
|
||||||
|
}
|
||||||
|
return p[:idx], p[idx+1:], nil
|
||||||
|
}
|
||||||
|
|
@ -28,7 +28,7 @@ func (i *WPAInterface) Scan(scanType string) error {
|
||||||
"Type": scanType, // "active" or "passive"
|
"Type": scanType, // "active" or "passive"
|
||||||
}
|
}
|
||||||
|
|
||||||
err := i.obj.Call(InterfaceInterface+".Scan", 0, args).Err
|
err := callNoAutoStart(i.obj, InterfaceInterface+".Scan", args).Err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("scan failed: %v", err)
|
return fmt.Errorf("scan failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +37,7 @@ func (i *WPAInterface) Scan(scanType string) error {
|
||||||
|
|
||||||
// GetBSSs returns a list of BSS (Basic Service Set) object paths
|
// GetBSSs returns a list of BSS (Basic Service Set) object paths
|
||||||
func (i *WPAInterface) GetBSSs() ([]dbus.ObjectPath, error) {
|
func (i *WPAInterface) GetBSSs() ([]dbus.ObjectPath, error) {
|
||||||
prop, err := i.obj.GetProperty(InterfaceInterface + ".BSSs")
|
prop, err := getPropNoAutoStart(i.obj, InterfaceInterface+".BSSs")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get BSSs property: %v", err)
|
return nil, fmt.Errorf("failed to get BSSs property: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ func (i *WPAInterface) GetBSSs() ([]dbus.ObjectPath, error) {
|
||||||
|
|
||||||
// GetState returns the current connection state
|
// GetState returns the current connection state
|
||||||
func (i *WPAInterface) GetState() (WPAState, error) {
|
func (i *WPAInterface) GetState() (WPAState, error) {
|
||||||
prop, err := i.obj.GetProperty(InterfaceInterface + ".State")
|
prop, err := getPropNoAutoStart(i.obj, InterfaceInterface+".State")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get State property: %v", err)
|
return "", fmt.Errorf("failed to get State property: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +67,7 @@ func (i *WPAInterface) GetState() (WPAState, error) {
|
||||||
|
|
||||||
// GetCurrentBSS returns the currently connected BSS object path
|
// GetCurrentBSS returns the currently connected BSS object path
|
||||||
func (i *WPAInterface) GetCurrentBSS() (dbus.ObjectPath, error) {
|
func (i *WPAInterface) GetCurrentBSS() (dbus.ObjectPath, error) {
|
||||||
prop, err := i.obj.GetProperty(InterfaceInterface + ".CurrentBSS")
|
prop, err := getPropNoAutoStart(i.obj, InterfaceInterface+".CurrentBSS")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get CurrentBSS property: %v", err)
|
return "", fmt.Errorf("failed to get CurrentBSS property: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +90,7 @@ func (i *WPAInterface) AddNetwork(config map[string]interface{}) (dbus.ObjectPat
|
||||||
dbusConfig[key] = dbus.MakeVariant(value)
|
dbusConfig[key] = dbus.MakeVariant(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := i.obj.Call(InterfaceInterface+".AddNetwork", 0, dbusConfig).Store(&networkPath)
|
err := callNoAutoStart(i.obj, InterfaceInterface+".AddNetwork", dbusConfig).Store(&networkPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to add network: %v", err)
|
return "", fmt.Errorf("failed to add network: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +100,7 @@ func (i *WPAInterface) AddNetwork(config map[string]interface{}) (dbus.ObjectPat
|
||||||
|
|
||||||
// SelectNetwork connects to a network
|
// SelectNetwork connects to a network
|
||||||
func (i *WPAInterface) SelectNetwork(networkPath dbus.ObjectPath) error {
|
func (i *WPAInterface) SelectNetwork(networkPath dbus.ObjectPath) error {
|
||||||
err := i.obj.Call(InterfaceInterface+".SelectNetwork", 0, networkPath).Err
|
err := callNoAutoStart(i.obj, InterfaceInterface+".SelectNetwork", networkPath).Err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to select network: %v", err)
|
return fmt.Errorf("failed to select network: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +110,7 @@ func (i *WPAInterface) SelectNetwork(networkPath dbus.ObjectPath) error {
|
||||||
// EnableNetwork marks a network configuration as enabled (eligible for auto-connect)
|
// EnableNetwork marks a network configuration as enabled (eligible for auto-connect)
|
||||||
func (i *WPAInterface) EnableNetwork(networkPath dbus.ObjectPath) error {
|
func (i *WPAInterface) EnableNetwork(networkPath dbus.ObjectPath) error {
|
||||||
netObj := i.conn.Object(Service, networkPath)
|
netObj := i.conn.Object(Service, networkPath)
|
||||||
err := netObj.SetProperty(NetworkInterface+".Enabled", dbus.MakeVariant(true))
|
err := setPropNoAutoStart(netObj, NetworkInterface+".Enabled", dbus.MakeVariant(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to enable network: %v", err)
|
return fmt.Errorf("failed to enable network: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -119,7 +119,7 @@ func (i *WPAInterface) EnableNetwork(networkPath dbus.ObjectPath) error {
|
||||||
|
|
||||||
// RemoveNetwork removes a network configuration
|
// RemoveNetwork removes a network configuration
|
||||||
func (i *WPAInterface) RemoveNetwork(networkPath dbus.ObjectPath) error {
|
func (i *WPAInterface) RemoveNetwork(networkPath dbus.ObjectPath) error {
|
||||||
err := i.obj.Call(InterfaceInterface+".RemoveNetwork", 0, networkPath).Err
|
err := callNoAutoStart(i.obj, InterfaceInterface+".RemoveNetwork", networkPath).Err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to remove network: %v", err)
|
return fmt.Errorf("failed to remove network: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +128,7 @@ func (i *WPAInterface) RemoveNetwork(networkPath dbus.ObjectPath) error {
|
||||||
|
|
||||||
// GetNetworks returns the object paths of all configured networks
|
// GetNetworks returns the object paths of all configured networks
|
||||||
func (i *WPAInterface) GetNetworks() ([]dbus.ObjectPath, error) {
|
func (i *WPAInterface) GetNetworks() ([]dbus.ObjectPath, error) {
|
||||||
prop, err := i.obj.GetProperty(InterfaceInterface + ".Networks")
|
prop, err := getPropNoAutoStart(i.obj, InterfaceInterface+".Networks")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get Networks property: %v", err)
|
return nil, fmt.Errorf("failed to get Networks property: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +143,7 @@ func (i *WPAInterface) GetNetworks() ([]dbus.ObjectPath, error) {
|
||||||
|
|
||||||
// Disconnect disconnects from the current network
|
// Disconnect disconnects from the current network
|
||||||
func (i *WPAInterface) Disconnect() error {
|
func (i *WPAInterface) Disconnect() error {
|
||||||
err := i.obj.Call(InterfaceInterface+".Disconnect", 0).Err
|
err := callNoAutoStart(i.obj, InterfaceInterface+".Disconnect").Err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("disconnect failed: %v", err)
|
return fmt.Errorf("disconnect failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +152,7 @@ func (i *WPAInterface) Disconnect() error {
|
||||||
|
|
||||||
// SaveConfig saves the current configuration to the wpa_supplicant config file
|
// SaveConfig saves the current configuration to the wpa_supplicant config file
|
||||||
func (i *WPAInterface) SaveConfig() error {
|
func (i *WPAInterface) SaveConfig() error {
|
||||||
err := i.obj.Call(InterfaceInterface+".SaveConfig", 0).Err
|
err := callNoAutoStart(i.obj, InterfaceInterface+".SaveConfig").Err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("save config failed: %v", err)
|
return fmt.Errorf("save config failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +166,7 @@ func (i *WPAInterface) GetPath() dbus.ObjectPath {
|
||||||
|
|
||||||
// GetScanning returns whether a scan is currently in progress
|
// GetScanning returns whether a scan is currently in progress
|
||||||
func (i *WPAInterface) GetScanning() (bool, error) {
|
func (i *WPAInterface) GetScanning() (bool, error) {
|
||||||
prop, err := i.obj.GetProperty(InterfaceInterface + ".Scanning")
|
prop, err := getPropNoAutoStart(i.obj, InterfaceInterface+".Scanning")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to get Scanning property: %v", err)
|
return false, fmt.Errorf("failed to get Scanning property: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ func (n *Network) GetPath() dbus.ObjectPath {
|
||||||
|
|
||||||
// GetProperties returns properties of the network configuration
|
// GetProperties returns properties of the network configuration
|
||||||
func (n *Network) GetProperties() (map[string]dbus.Variant, error) {
|
func (n *Network) GetProperties() (map[string]dbus.Variant, error) {
|
||||||
prop, err := n.obj.GetProperty(NetworkInterface + ".Properties")
|
prop, err := getPropNoAutoStart(n.obj, NetworkInterface+".Properties")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue