mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-03 12:08:31 -04:00
ca89e12963
* core: fix security and concurrency issues found in backend audit Security: - privesc: pipe the sudo password via stdin (sudo -S) instead of embedding it in the command string, so it no longer appears in argv (readable by any local user via /proc/<pid>/cmdline or ps) - greeter: tokenize a session .desktop Exec= line into argv and execve directly instead of running it through /bin/sh -c, closing a command- injection path via user-writable ~/.local/share/wayland-sessions - plugins: reject path-separator/.. in plugin id/name before joining into a filesystem path, closing an arbitrary-directory-delete in the uninstall/update fallback - keybinds/hyprland: always quote unrecognized bind actions/keys when writing generated Lua; only re-emit genuine round-tripped custom Lua verbatim (tracked via an explicit flag), closing a Lua-injection path - desktop/mimeapps: reject newline/bracket in mime/desktop-id fields so they can't inject fake sections into the shared mimeapps.list Robustness / concurrency: - server: recover panics in the request-dispatch path so one bad handler can't crash the daemon and drop every client - go-wayland: recover panics in the shared dispatch choke point so a malformed compositor event can't crash CLI tools / the daemon - server: per-connection D-Bus client ID instead of a shared constant, fixing cross-client signal delivery and subscription teardown - network: guard the NetworkManager device maps with a mutex (a concurrent map read/write here is an unrecoverable fatal error) - cups: close the event channel on Stop() so Unsubscribe() of the last subscriber no longer deadlocks; allocate the fresh channel in Start() - freedesktop: reuse the shared session conn for the settings watcher and tear it down in Close(), fixing a per-Manager conn+goroutine leak - clipboard: mutex-guard lazy dbusConn creation - geolocation: use WithMatchMember for the GeoClue2 LocationUpdated signal (was WithMatchSender with an interface.member string, so the match never fired and live location updates never arrived) - screenshot: set failed=true on buffer/pool creation errors so the dispatch loop doesn't wait forever for a ready/failed that never comes * apply code review comments --------- Co-authored-by: bbedward <bbedward@gmail.com>
242 lines
5.7 KiB
Go
242 lines
5.7 KiB
Go
package dbus
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/params"
|
|
)
|
|
|
|
type objectParams struct {
|
|
bus string
|
|
dest string
|
|
path string
|
|
iface string
|
|
}
|
|
|
|
func extractObjectParams(p map[string]any, requirePath bool) (objectParams, error) {
|
|
bus, err := params.String(p, "bus")
|
|
if err != nil {
|
|
return objectParams{}, err
|
|
}
|
|
dest, err := params.String(p, "dest")
|
|
if err != nil {
|
|
return objectParams{}, err
|
|
}
|
|
|
|
var path string
|
|
if requirePath {
|
|
path, err = params.String(p, "path")
|
|
if err != nil {
|
|
return objectParams{}, err
|
|
}
|
|
} else {
|
|
path = params.StringOpt(p, "path", "/")
|
|
}
|
|
|
|
iface, err := params.String(p, "interface")
|
|
if err != nil {
|
|
return objectParams{}, err
|
|
}
|
|
|
|
return objectParams{bus: bus, dest: dest, path: path, iface: iface}, nil
|
|
}
|
|
|
|
func HandleRequest(conn net.Conn, req models.Request, m *Manager, clientID string) {
|
|
switch req.Method {
|
|
case "dbus.call":
|
|
handleCall(conn, req, m)
|
|
case "dbus.getProperty":
|
|
handleGetProperty(conn, req, m)
|
|
case "dbus.setProperty":
|
|
handleSetProperty(conn, req, m)
|
|
case "dbus.getAllProperties":
|
|
handleGetAllProperties(conn, req, m)
|
|
case "dbus.introspect":
|
|
handleIntrospect(conn, req, m)
|
|
case "dbus.listNames":
|
|
handleListNames(conn, req, m)
|
|
case "dbus.subscribe":
|
|
handleSubscribe(conn, req, m, clientID)
|
|
case "dbus.unsubscribe":
|
|
handleUnsubscribe(conn, req, m)
|
|
default:
|
|
models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
|
|
}
|
|
}
|
|
|
|
func handleCall(conn net.Conn, req models.Request, m *Manager) {
|
|
op, err := extractObjectParams(req.Params, true)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
method, err := params.String(req.Params, "method")
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
var args []any
|
|
if argsRaw, ok := params.Any(req.Params, "args"); ok {
|
|
if argsSlice, ok := argsRaw.([]any); ok {
|
|
args = argsSlice
|
|
}
|
|
}
|
|
|
|
result, err := m.Call(op.bus, op.dest, op.path, op.iface, method, args)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
models.Respond(conn, req.ID, result)
|
|
}
|
|
|
|
func handleGetProperty(conn net.Conn, req models.Request, m *Manager) {
|
|
op, err := extractObjectParams(req.Params, true)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
property, err := params.String(req.Params, "property")
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
result, err := m.GetProperty(op.bus, op.dest, op.path, op.iface, property)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
models.Respond(conn, req.ID, result)
|
|
}
|
|
|
|
func handleSetProperty(conn net.Conn, req models.Request, m *Manager) {
|
|
op, err := extractObjectParams(req.Params, true)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
property, err := params.String(req.Params, "property")
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
value, ok := params.Any(req.Params, "value")
|
|
if !ok {
|
|
models.RespondError(conn, req.ID, "missing 'value' parameter")
|
|
return
|
|
}
|
|
|
|
if err := m.SetProperty(op.bus, op.dest, op.path, op.iface, property, value); err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
models.Respond(conn, req.ID, models.SuccessResult{Success: true})
|
|
}
|
|
|
|
func handleGetAllProperties(conn net.Conn, req models.Request, m *Manager) {
|
|
op, err := extractObjectParams(req.Params, true)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
result, err := m.GetAllProperties(op.bus, op.dest, op.path, op.iface)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
models.Respond(conn, req.ID, result)
|
|
}
|
|
|
|
func handleIntrospect(conn net.Conn, req models.Request, m *Manager) {
|
|
bus, err := params.String(req.Params, "bus")
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
dest, err := params.String(req.Params, "dest")
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
path := params.StringOpt(req.Params, "path", "/")
|
|
|
|
result, err := m.Introspect(bus, dest, path)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
models.Respond(conn, req.ID, result)
|
|
}
|
|
|
|
func handleListNames(conn net.Conn, req models.Request, m *Manager) {
|
|
bus, err := params.String(req.Params, "bus")
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
result, err := m.ListNames(bus)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
models.Respond(conn, req.ID, result)
|
|
}
|
|
|
|
func handleSubscribe(conn net.Conn, req models.Request, m *Manager, clientID string) {
|
|
if id := params.StringOpt(req.Params, "clientId", ""); id != "" {
|
|
clientID = id
|
|
}
|
|
|
|
bus, err := params.String(req.Params, "bus")
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
sender := params.StringOpt(req.Params, "sender", "")
|
|
path := params.StringOpt(req.Params, "path", "")
|
|
iface := params.StringOpt(req.Params, "interface", "")
|
|
member := params.StringOpt(req.Params, "member", "")
|
|
|
|
result, err := m.Subscribe(clientID, bus, sender, path, iface, member)
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
models.Respond(conn, req.ID, result)
|
|
}
|
|
|
|
func handleUnsubscribe(conn net.Conn, req models.Request, m *Manager) {
|
|
subID, err := params.String(req.Params, "subscriptionId")
|
|
if err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
if err := m.Unsubscribe(subID); err != nil {
|
|
models.RespondError(conn, req.ID, err.Error())
|
|
return
|
|
}
|
|
|
|
models.Respond(conn, req.ID, models.SuccessResult{Success: true})
|
|
}
|