package iwd import ( "fmt" "sync" "github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5/introspect" ) const agentIntrospectXML = ` ` // Agent implements the net.connman.iwd.Agent interface for credential callbacks type Agent struct { conn *dbus.Conn path dbus.ObjectPath passphraseStore map[string]string mu sync.RWMutex } // NewAgent creates a new Agent instance func NewAgent(conn *dbus.Conn, path dbus.ObjectPath) *Agent { return &Agent{ conn: conn, path: path, passphraseStore: make(map[string]string), } } // SetPassphrase stores a passphrase for a given network SSID func (a *Agent) SetPassphrase(ssid, passphrase string) { a.mu.Lock() defer a.mu.Unlock() a.passphraseStore[ssid] = passphrase } // ClearPassphrase removes the stored passphrase for a network func (a *Agent) ClearPassphrase(ssid string) { a.mu.Lock() defer a.mu.Unlock() delete(a.passphraseStore, ssid) } // Export registers the agent object on D-Bus func (a *Agent) Export() error { err := a.conn.Export(a, a.path, AgentInterface) if err != nil { return fmt.Errorf("failed to export agent: %v", err) } err = a.conn.Export(introspect.Introspectable(agentIntrospectXML), a.path, "org.freedesktop.DBus.Introspectable") if err != nil { a.conn.Export(nil, a.path, AgentInterface) return fmt.Errorf("failed to export introspection: %v", err) } return nil } // Unexport unregisters the agent from D-Bus func (a *Agent) Unexport() { a.conn.Export(nil, a.path, AgentInterface) a.conn.Export(nil, a.path, "org.freedesktop.DBus.Introspectable") } // getNetworkSSID queries the network object to get its SSID (Name property) func (a *Agent) getNetworkSSID(networkPath dbus.ObjectPath) (string, error) { obj := a.conn.Object(Service, networkPath) variant, err := obj.GetProperty(NetworkInterface + ".Name") if err != nil { return "", fmt.Errorf("failed to get network name: %v", err) } name, ok := variant.Value().(string) if !ok { return "", fmt.Errorf("network name is not a string") } return name, nil } // RequestPassphrase is called by iwd when connecting to PSK networks func (a *Agent) RequestPassphrase(network dbus.ObjectPath) (string, *dbus.Error) { fmt.Printf("[Agent] RequestPassphrase called for network: %s\n", network) ssid, err := a.getNetworkSSID(network) if err != nil { fmt.Printf("[Agent] Failed to get SSID: %v\n", err) return "", dbus.MakeFailedError(fmt.Errorf("failed to get network SSID: %v", err)) } fmt.Printf("[Agent] Network SSID: %s\n", ssid) a.mu.RLock() passphrase, ok := a.passphraseStore[ssid] a.mu.RUnlock() if !ok { fmt.Printf("[Agent] No passphrase stored for SSID: %s\n", ssid) return "", dbus.MakeFailedError(fmt.Errorf("no passphrase stored for network '%s'", ssid)) } fmt.Printf("[Agent] Returning passphrase for SSID: %s\n", ssid) return passphrase, nil } // RequestPrivateKeyPassphrase is called for encrypted private keys func (a *Agent) RequestPrivateKeyPassphrase(network dbus.ObjectPath) (string, *dbus.Error) { // Not implemented for now return "", dbus.MakeFailedError(fmt.Errorf("RequestPrivateKeyPassphrase not implemented")) } // RequestUserNameAndPassword is called for enterprise networks func (a *Agent) RequestUserNameAndPassword(network dbus.ObjectPath) (string, string, *dbus.Error) { // Not implemented for now return "", "", dbus.MakeFailedError(fmt.Errorf("RequestUserNameAndPassword not implemented")) } // RequestUserPassword is called for enterprise networks with known username func (a *Agent) RequestUserPassword(network dbus.ObjectPath, user string) (string, *dbus.Error) { // Not implemented for now return "", dbus.MakeFailedError(fmt.Errorf("RequestUserPassword not implemented")) } // Cancel is called when a request is canceled func (a *Agent) Cancel(reason string) *dbus.Error { // Nothing to do, just acknowledge return nil } // Release is called when the agent is unregistered func (a *Agent) Release() *dbus.Error { // Cleanup if needed a.mu.Lock() a.passphraseStore = make(map[string]string) a.mu.Unlock() return nil }