mrrl-logincaps

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

commit ad8f64c38c4e3a6f8c63a62829c46b8a8ed863b5
parent 04a791423b38f16c683c19263a55e7ee3a3121df
Author: Jan Pobrislo <ccx@webprojekty.cz>
Date:   Tue, 15 Dec 2020 02:26:06 +0100

Add command to launch privileged app in user terminal without prompting for password.
Diffstat:
Msbin/logincaps | 9+++++++++
Asbin/user-term.py | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 104 insertions(+), 0 deletions(-)

diff --git a/sbin/logincaps b/sbin/logincaps @@ -79,9 +79,11 @@ main() { (o) printf >&2 "%s\n" "Would power-off." cap_cmd s6-poweroff ;; + (b) printf >&2 "%s\n" "Would reboot." cap_cmd s6-reboot ;; + (terminal *) term_cmd=( "${(Q@)${(z)${line#terminal }}}" ) printf >&2 "%s\n" "Would launch root terminal." @@ -91,6 +93,13 @@ main() { printf 'OK\n' ;; + (terminal-wpa_cli) + term_cmd=( "${(Q@)${(z)${line#terminal-wpa_cli }}}" ) + printf >&2 "%s\n" "Would launch terminal with wpa_cli." + /command/user-term.py -u $USER -- ' wpa_cli' '' "$term_cmd[@]" </dev/null >&2 &! + printf 'OK\n' + ;; + (chvt tty) cap_cmd chvt ${LOGIN_TTY#/dev/tty} ;; diff --git a/sbin/user-term.py b/sbin/user-term.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +from __future__ import ( + generators, division, absolute_import, with_statement, print_function +) +import sys +import os +import os.path +import pwd +import argparse +import re + +parser = argparse.ArgumentParser(description="Runs one program as current user in pty and another with changed privileges with master pty fd") +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.add_argument('term_env', help='The TERM variable used by the slave') +parser.add_argument('exe', nargs='+', help='Execline block defining program ran on slave end, then terminal program') + + +def exec_terminal(uid, gid, home, terminal, term_env, slave_exe): + assert isinstance(uid, int) + assert isinstance(gid, int) + assert isinstance(home, str) + assert isinstance(terminal, (list, tuple)) + assert all(isinstance(s, str) for s in terminal) + assert len(terminal) + assert isinstance(shell, str) + master, slave = os.openpty() + if os.fork(): + # parent + 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) + else: + # child + os.close(master) + os.dup2(slave, 0) + os.dup2(slave, 1) + os.dup2(slave, 2) + os.close(slave) + os.setsid() + env = dict(os.environ) + env['TERM'] = term_env + env.pop('LOGNAME', None) + os.execve('/bin/exec', ['exec'] + slave_exe, env) + + +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) + + argv = list(args.exe) + slave_argv = [] + while argv: + a = argv.pop(0) + if a == '': + break + elif a[0] == ' ': + slave_argv.append(a[1:]) + else: + sys.stderr.write("Improperly terminated block!\n") + return 1 + if not argv: + sys.stderr.write("Terminal executable not specified.\n") + return 1 + + exec_terminal( + uid=uid, + gid=gid, + home=pwent.pw_dir, + terminal=argv, + slave_exe=slave_arv, + term_env=args.term_env, + ) + + +if __name__ == '__main__': + main()