mrrl-logincaps

MRRL version of logincaps
git clone https://ccx.te2000.cz/git/mrrl-logincaps
Log | Files | Refs

commit fb0670d41fc9e72c119698a5fd5ae5d45f655e69
parent e9cac0551b174f25d4a2c9fb50d9ac4b142b7c67
Author: Jan Pobrislo <ccx@webprojekty.cz>
Date:   Tue, 15 Dec 2020 04:47:32 +0100

Remove uid-changing code from su-term.py, rely on chained commands instead.
Diffstat:
Mbin/zshaskpass_lock | 15+++++++++++----
Msbin/logincaps | 42++++++++++++++++++++++++------------------
Msbin/su-term.py | 41+++++++++++++++--------------------------
3 files changed, 50 insertions(+), 48 deletions(-)

diff --git a/bin/zshaskpass_lock b/bin/zshaskpass_lock @@ -120,14 +120,20 @@ cmd() { logincap b;; (root) # logincap "terminal rxvt-unicode importas -i PTY_FD PTY_FD s6-envdir /run/user/ccx/X$[ ${TTY#/dev/tty} + 4 ]/env urxvt -pty-fd \$PTY_FD" ;; - logincap "terminal $terminal_env $terminal_el" ;; + logincap "terminal $terminal_env $terminal_el" + logincap chvt X + ;; (t *) - logincap "terminal-${1#t } $terminal_env $terminal_el" ;; + logincap "terminal-${1#t } $terminal_env $terminal_el" + logincap chvt X + ;; (lock) - lock;; + lock + ;; (exit) true >/run/user/ccx/X$[ ${TTY#/dev/tty} + 4 ]/fifo - keep_running=0;; + keep_running=0 + ;; (shell) zsh -i; printf "shell exited with %d\n" $?;; (*) printf '%s: unknown command\n' ${(qqq)REPLY};; @@ -149,6 +155,7 @@ read-plumber-message() { case $plumb_data in (poweroff) logincap o;; (reboot) logincap b;; + (terminal-*) logincap "${plumb_data%% *} $terminal_env $terminal_el";; (lock) logincap chvt tty lock diff --git a/sbin/logincaps b/sbin/logincaps @@ -73,38 +73,44 @@ cap_cmd() { } typeset -f -t cap_cmd +terminal_spawn_wrapper() { + local term_cmd term_env + term_cmd="${2#* }" + term_env=${term_cmd%% *} + if ! [[ $term_env =~ [-.0-9a-zA-Z]* ]]; then + printf 'ERR: invalid TERM' + return 1 + fi + term_cmd="${term_cmd#* }" + cap_cmd execlineb -c "$1 -- ${(qqq)term_env} cd / s6-setuidgid ${(qqq)USER} $term_cmd" +} + +terminal_spawn_password() { + terminal_spawn_wrapper /command/su-term.py "$@" +} + +terminal_spawn() { + terminal_spawn_wrapper /command/spawn-pty.py "$@" +} + main() { local line term_cmd term_env REPLY while read line; do case $line in - (o) printf >&2 "%s\n" "Would power-off." + (o) cap_cmd s6-poweroff ;; - (b) printf >&2 "%s\n" "Would reboot." + (b) cap_cmd s6-reboot ;; (terminal *) - term_cmd=( "${(Q@)${(z)${line#terminal }}}" ) - printf >&2 "%s\n" "Would launch root terminal." - pretendrun /command/su-term.py -d 1 -u $USER -g $USER -- "$term_cmd[@]" <&2 - #{ /command/su-term.py -d 1 -u $USER -- "$term_cmd[@]" <&2 &! - #} | read - printf 'OK\n' + terminal_spawn_password "$line" ;; (terminal-wpa_cli *) - term_cmd="${line#* }" - term_env=${term_cmd%% *} - if ! [[ $term_env =~ [-.0-9a-zA-Z]* ]]; then - printf 'ERR: invalid TERM' - continue - fi - term_cmd="${term_cmd#* }" - cap_cmd execlineb -c "/command/spawn-pty.py -- ${(qqq)term_env} { wpa_cli } cd / s6-setuidgid ${(qqq)USER} $term_cmd" - #/command/user-term.py -u $USER -g $USER -- "$term_cmd[1]" ' wpa_cli' '' "${(@)term_cmd[2,-1]}" </dev/null >&2 &! - printf 'OK\n' + terminal_spawn "$line" ;; (chvt tty) diff --git a/sbin/su-term.py b/sbin/su-term.py @@ -13,18 +13,25 @@ import getpass import argparse import re -parser = argparse.ArgumentParser(description="Check root password and run terminal as given user with root shell") -parser.add_argument('-u', '--user', required=True, help="User name or ID to run terminal as") -parser.add_argument('-g', '--group', type=int, help="Group ID to run terminal as") +parser = argparse.ArgumentParser(description="Check root password and run terminal with root shell") parser.add_argument('-d', '--notify-fd', type=int, help="Write newline to this filedescriptor after reading password") parser.add_argument('term_env', help='The TERM variable used by the shell') parser.add_argument('exe', nargs='+', help='Terminal application to execute') -def exec_terminal(uid, gid, home, terminal, term_env, shell, shell_workdir): - assert isinstance(uid, int) - assert isinstance(gid, int) - assert isinstance(home, str) +def execve(argv, env): + if '/' in argv[0]: + os.execve(argv[0], argv, env) + else: + for p in os.environ['PATH'].split(os.path.pathsep): + try: + os.execve(os.path.join(p, argv[0]), argv, env) + except OSError: + continue + raise SystemExit(1) + + +def exec_terminal(terminal, term_env, shell, shell_workdir): assert isinstance(terminal, (list, tuple)) assert all(isinstance(s, str) for s in terminal) assert len(terminal) @@ -35,17 +42,9 @@ def exec_terminal(uid, gid, home, terminal, term_env, shell, shell_workdir): os.close(slave) os.dup2(master, 0) os.close(master) - os.setgid(gid) - os.setuid(uid) - os.chdir(home) env = dict(os.environ) env['PTY_FD'] = "0" - env['HOME'] = home - if '/' in terminal[0]: - os.execve(terminal[0], terminal, env) - else: - # TODO: proper search path - os.execve('/bin/exec', ['exec'] + terminal, env) + execve(terminal, env) else: # child os.close(master) @@ -84,13 +83,6 @@ def constant_time_compare(val1, val2): def main(): args = parser.parse_args() - if re.match('[0-9]+', args.user): - uid = int(args.user) - pwent = pwd.getpwuid(uid) - else: - pwent = pwd.getpwnam(args.user) - uid = pwent.pw_uid - gid = pwent.pw_gid if args.group is None else int(args.group) root_hash = spwd.getspnam('root').sp_pwd root_pwent = pwd.getpwnam('root') p = getpass.getpass() @@ -99,9 +91,6 @@ def main(): if args.notify_fd is not None: os.write(args.notify_fd, '\n') exec_terminal( - uid=uid, - gid=gid, - home=pwent.pw_dir, terminal=args.exe, term_env=args.term_env, shell=root_pwent.pw_shell,