On Sat, 09 Feb 2019 at 23:26:53 +0100, Francesco Poli wrote: > On Sat, 9 Feb 2019 22:31:14 +0100 Michael Biebl wrote: > > I guess I already mentioned the two alternatives (runuser/setpriv). > [...] > > Maybe setpriv is equivalent to s6-setuidgid. > If this is the case, it can be used as an alternative to s6-setuidgid.
As far as I can tell, s6-setuidgid sets the uid, primary gid and supplementary group list with setuid(), setgid() and setgroups() (by a somewhat convoluted route involving putting them in the environment with s6-envuidgid and reading them back out in s6-applyuidgid, presumably out of a preference for small composable tools), and doesn't do anything else of interest (for example resetting HOME or LOGNAME to reflect the new identity, which it should perhaps do). Running `setpriv --reuid NAME --init-groups PROGRAM ARGS` appears to be equivalent to `s6-setuidgid NAME PROGRAM ARGS`. This is because on Linux (and hopefully the non-Linux ports too), setuid(x) called by a privileged process is documented to be equivalent to setresuid(x, x, x), which is exactly what setpriv --reuid ends up doing; and setpriv --init-groups takes the `struct passwd` for the NAME given to --reuid (or --ruid), and sets the primary gid and supplementary group list based on it, just like the combination of s6-envuidgid and s6-applyuidgid. If you don't consider the current use of s6-setuidgid to be a bug, then using setpriv would have an equivalent effect without a non-Essential dependency, closing Bug #921819 and the very similar bug #916753. Anything beyond that (for example altering DISPLAY, HOME, LOGNAME, etc. to get an environment more closely resembling the target user's login shell) is a separate request for enhancement that is not solved by s6-setuidgid either, so please don't let the perfect be the enemy of the good here. > I would like some insight especially on [message #30], regarding the > fact that runuser does something basically equivalent to what su does, > and thus seems to be unfit to irreversibly drop root privileges The major difference between {setpriv,s6-setuidgid} and runuser is that runuser, like su, sets up a new PAM session and re-initializes some standard environment variables. Also like su, if run with -l, it also tries to behave like a login shell (clears more environment variables, changes to the target user's home directory, etc.) and runs a different PAM stack (which registers with systemd-logind, if installed, as a new login session). However, runuser is not setuid (unlike su), so it cannot increase privileges, only drop them. The only thing it *can* do is to drop root privileges; so if you consider it to be unfit to do that for some reason, it would have no purpose at all. I assume the maintainers of util-linux are not in the habit of adding tools that have no purpose. > and > regarding my search for a command that works like s6-setuidgid, but > runs the given command inside the user's login shell (with all the > environment that the user would get on a normal login). As stated, this isn't really well-defined. Whether and how this is possible depends what you mean by "a normal login". The execution environment the user would get from login(1) as invoked by getty(8), from a display manager like xdm, and from sshd are all different (they invoke different PAM stacks); and that's before you've even entered any shells. Running a given command in a user's login shell also suffers from the fact that you don't actually know what the user's login shell does. It isn't guaranteed to be a POSIX shell, or a Bourne-compatible shell, or in any way suitable for non-interactive use, and it isn't guaranteed to take the same command-line arguments as dash and bash. > But I would like to have a command that runs a given command inside the > regular user's login environment, as I said above. > Do you know one such command? The tl;dr answer is "no". The slightly longer answer is "it's complicated". If your design relies on being able to do this, it might be a good idea to rethink aspects of your design. For instance, instead of bending over backwards to run a web browser as the invoking user, it might be reasonable to print a file:/// URL to the terminal, and hope the invoking user has a terminal in which they can Ctrl+click URLs? Recent versions of some terminals even have an escape sequence to mark arbitrary text as a clickable URL, as used by `ls --hyperlink`. The long answer starts with: this is not really how it works. You can't unilaterally move a child process from your own execution environment into a logged-in user's execution environment: whatever you do, it will inherit the inheritable aspects of its execution environment from you, not from the user. Historically, the inheritable aspects of the execution environment were sufficiently well-known and sufficiently few in number (for instance credentials, environment variables and working directory) that you could get away with resetting them to known-good (or at least conjectured-to-be-good) values and hoping it was enough. However, a modern Linux process has an ever-increasing number of attributes that are inherited from its parent (open file descriptors, rlimits, capabilities, namespaces, seccomp bits, cgroups, audit loginuid, LSM contexts...), some of which are explicitly designed to be impossible to forge, or set once/impossible to unset, or similar; so that doesn't really work any more, if it ever did. The reason that some of these newer aspects of the execution environment are designed to be set once/impossible to unset is that you can't abuse privileges you can't gain. Privilege escalation via mechanisms like setuid is inherently dangerous, because all the inheritable process attributes of the setuid process are inherited from a potential attacker, and the setuid process is responsible for making sure that none of them can be used to trick it into misusing its privileges. Earlier, I talked about getty(8)/login(1), xdm and sshd. All of those have something in common: they are "entry point" services through which users log in. More specifically, they start as a system-level process, outside the context of any particular user login session (usually), and they create a user login session (for their child process, or for a process that replaces them in exec()). In a system with pam_loginuid ("audit sessions") enabled, system-level processes have their loginuid attribute set to -1, while user-session processes have their loginuid set to the uid of the user whose session it is. After setting the loginuid to a value other than -1, it cannot change. In a system booted with systemd, there is another way in which system-level processes and user login sessions are disjoint: system-level processes are in the system.slice cgroup, user-level processes are in user.slice, system-level processes can move their children into user.slice, and user-level processes cannot normally move their children back into system.slice (although suitably privileged user-level processes *can* use IPC to ask a system-level process like systemd to create more system-level children). On a system booted with sysvinit, things are a bit more blurry: if a logged-in sysadmin runs "/etc/init.d/ssh restart", sshd becomes a child of their user session (its loginuid is set to the sysadmin's), and because you can't escape from a user session, all subsequent ssh sessions are formally part of the sysadmin's user session (which seems vanishingly unlikely to be what they meant). The same applies to xdm and other display managers. The closest you can get to executing an arbitrary command in a user's session is to use IPC to tell a process that is already associated with the user's login session to run an equivalent command for you. For instance, on a system that was booted with systemd and has working libpam-systemd and dbus-user-session packages, you could do the equivalent of something like this (totally untested): numeric_uid=$(id -u $username) if [ -d /run/user/$numeric_uid/systemd ] \ && [ -S /run/user/$numeric_uid/bus ]; then setpriv --reuid $username --initgroups \ env \ -u DBUS_SESSION_BUS_ADDRESS \ XDG_RUNTIME_DIR="/run/user/$numeric_uid" \ systemd-run --user --wait COMMAND [ARGS...] else ... this is not one of those systems, so do something else ... fi In this example, systemd-run is a child of the shell script, and has the user's credentials (uid and groups) but remains outside their session - but the COMMAND that it launches via IPC is a child of their `systemd --user` process, and inherits its execution environment from there instead. You can think of it as being a bit like a ssh client logging in to a remote system to run the COMMAND. That snippet depends on the implementation detail that systemd sets user processes' XDG_RUNTIME_DIR to /run/user/$numeric_uid, which in principle it would be within its rights to change at any time, but in practice there's no reason why it would. It also makes use of the fact that systemd-run uses a D-Bus socket in the XDG_RUNTIME_DIR to communicate with systemd --user, and that systemd-run doesn't strongly depend on other environment variables having their expected values. You could also do this by attaching a ptrace-based debugger to a process in the user session and forcing it to fork-and-exec the desired command... but don't do that. The way this is *meant* to work in recent desktop environments is that you don't inject arbitrary commands into user sessions "from the outside". Instead, a per-user service or application inside the user session uses IPC (D-Bus or similar) to subscribe to signals from a system service, and the system service notifies interested clients when something interesting happens. For instance, this is how the needrestart-session package works, and is also how UIs for system services like NetworkManager and wicd work. apt-listbugs is in a particularly precarious situation because apt is often run from inside a user session, under a (setuid!) privilege-escalation tool like su, sudo or pkexec. So the process hierarchy looks something like this: [init, etc.] \- xdm or sshd or whatever **** \- [multiple levels of user session processes] \- xterm or similar \- sudo apt-get update **** \- [apt stuff] \- apt-listbugs \- the bit we're now talking about **** \- firefox where all the lines marked **** are either dropping or escalating privileges. smcv