Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion files/usr/bin/cinnamon-settings-users
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,39 @@
"""

import os
import shutil
import sys

os.system("pkexec /usr/share/cinnamon/cinnamon-settings-users/cinnamon-settings-users.py")
TARGET = "/usr/share/cinnamon/cinnamon-settings-users/cinnamon-settings-users.py"

def _build_run0_argv(target):
# Unlike pkexec, run0 does not inherit the caller's environment, so
# explicitly forward the few variables a GUI tool needs. Mirrors the
# approach used by gparted.
argv = ["run0"]
# --setenv=VAR (no value) tells run0 to forward the value from the caller.
if "XAUTHORITY" in os.environ:
argv.append("--setenv=XAUTHORITY")
if "DISPLAY" in os.environ:
argv.append("--setenv=DISPLAY")
# WAYLAND_DISPLAY must be turned into an absolute path because run0
# changes XDG_RUNTIME_DIR to that of the target user.
wayland = os.environ.get("WAYLAND_DISPLAY")
runtime_dir = os.environ.get("XDG_RUNTIME_DIR")
if wayland and runtime_dir and os.path.isdir(runtime_dir):
socket_path = wayland if os.path.isabs(wayland) else os.path.join(runtime_dir, wayland)
if os.path.exists(socket_path):
argv.append(f"--setenv=WAYLAND_DISPLAY={socket_path}")
argv.append("--")
argv.append(target)
return argv

# Prefer pkexec, fall back to run0.
if shutil.which("pkexec"):
os.execvp("pkexec", ["pkexec", TARGET])
elif shutil.which("run0"):
os.execvp("run0", _build_run0_argv(TARGET))

print("cinnamon-settings-users: neither 'pkexec' nor 'run0' was found in PATH",
file=sys.stderr)
sys.exit(1)
51 changes: 49 additions & 2 deletions files/usr/share/cinnamon/cinnamon-settings/bin/SettingsWidgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,43 @@

CAN_BACKEND = ["SoundFileChooser", "DateChooser", "TimeChooser", "Keybinding"]

# Prefer pkexec, fall back to run0.
def get_privilege_escalation_command():
for cmd in ("pkexec", "run0"):
if GLib.find_program_in_path(cmd) is not None:
return cmd
return None

def _build_run0_argv(command_argv):
# Unlike pkexec, run0 does not inherit the caller's environment, so
# explicitly forward the few variables a GUI tool needs. Mirrors the
# approach used by gparted.
argv = ["run0"]
# --setenv=VAR (no value) tells run0 to forward the value from the caller.
if GLib.getenv("XAUTHORITY") is not None:
argv.append("--setenv=XAUTHORITY")
if GLib.getenv("DISPLAY") is not None:
argv.append("--setenv=DISPLAY")
# WAYLAND_DISPLAY must be turned into an absolute path because run0
# changes XDG_RUNTIME_DIR to that of the target user.
wayland = GLib.getenv("WAYLAND_DISPLAY")
runtime_dir = GLib.getenv("XDG_RUNTIME_DIR")
if wayland and runtime_dir and os.path.isdir(runtime_dir):
socket_path = wayland if os.path.isabs(wayland) else os.path.join(runtime_dir, wayland)
if os.path.exists(socket_path):
argv.append(f"--setenv=WAYLAND_DISPLAY={socket_path}")
argv.append("--")
argv.extend(command_argv)
return argv

def build_privileged_argv(command_argv):
escalation = get_privilege_escalation_command()
if escalation is None:
return None
if escalation == "run0":
return _build_run0_argv(command_argv)
return [escalation, *command_argv]

class BinFileMonitor(GObject.GObject):
__gsignals__ = {
'changed': (GObject.SignalFlags.RUN_LAST, None, ()),
Expand Down Expand Up @@ -218,7 +255,12 @@ def build(self):
self.module.loaded = True

if self.is_standalone:
subprocess.Popen(self.exec_name.split())
argv = self.exec_name.split()
if argv and argv[0] == "pkexec":
privileged = build_privileged_argv(argv[1:])
if privileged is not None:
argv = privileged
subprocess.Popen(argv)
return

# Add our own widgets
Expand Down Expand Up @@ -297,7 +339,12 @@ def __init__(self, label, mod_id, icon, category, keywords, content_box):
self.category = category

def process (self):
name = self.name.replace("pkexec ", "")
name = self.name
if name.startswith("pkexec "):
# Require some privilege-escalation tool to exist (pkexec or run0).
if get_privilege_escalation_command() is None:
return False
name = name[len("pkexec "):]
name = name.split()[0]

return GLib.find_program_in_path(name) is not None
Expand Down
Loading