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:
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()