About
This post takes a look at a recent addition to FreeBSD: mdo(1). The mdo utility allows a given user or group member to execute a command as another user.
History
Historically UNIX programs that allow one user execute a command as another user (for example: doas(1), sudo(8)) have been implemented with a setuid binary that is owned by root.
When a setuid binary is executed by another user, it initially runs with an effective user ID of the user who owns the binary and that user will often be root. Once the setuid binary is run with the effective user id of 0, programs like doas(1) and sudo(8) will execute a system call similar to (or the same as) setuid(2) to drop privileges to the "target" user.
After privileges have been dropped to the target user, the real and effective user IDs along with the real and effective group IDs associated with the process would have been changed to match the "target" user.
We're now at the last stage where the system call execve(2) is executed, and a new process replaces the current process but with the privileges of the "target" user. That's the approach that has been used for decades, and it continues to be used today.
However, mdo(1) is different. It does not depend on a setuid binary at all. Instead, it depends on a mac policy (implemented as a kernel module) that can allow an unprivileged user to transition from one (effective|real) user/group ID to another (effective|real) user/group ID through the setcred(2) system call.
This difference sets mdo apart from other tools in the same space ( where traditionally a setuid binary was used).
Policy
Context
The mdo(1) utility depends on the mac_do(4) policy. This policy must be loaded into the kernel before mdo(1) can be used from the command line. The mac(4) man page provides more information about FreeBSD's Mandatory Access Control (MAC) framework.
Temporary
We can quickly try mdo without rebooting the system by loading the policy temporarily but the recommended approach is to load the policy earlier in the boot process:
root@freebsd# kldload mac_do
Persistent
We can choose to have the mdo policy persist across system reboots and
loaded into the kernel early in the boot process by updating
/boot/loader.conf. This is the recommended approach:
# /boot/loader.conf
mac_do_load="YES"
Rules
Context
A rule defines which user or group members can execute a command as
another user. These rules are stored as a semicolon-separated list and
can be managed via sysctl(8)
under the security.mac.do.rules node.
Each rule consists of two parts: a left-hand side and a right-hand
side, separated by the > character. The left-hand side
describes the source user ID or group ID that is allowed to run a
command, and the right-hand side describes the target ID that can be
inherited by user(s) described in the left-hand side of the rule.
Although security.mac.do.rules is roughly
analogous to OpenBSD's doas.conf file it currently cannot
express the same rules. For example, the doas.conf file lets you choose
which program can be executed and with what arguments where as mdo(1)
rules are focused purely on what id transitions are allowed.
Let's look at some examples of how to define rules:
root@freebsd# sysctl security.mac.do.rules="uid=$(id -u alice)>uid=$(id -u bob)"
root@freebsd# gid=$(getent group beatles | awk -F: '{print $3}')
root@freebsd# sysctl security.mac.do.rules="gid=${gid}>uid=$(id -u bob)"
root@freebsd# sysctl security.mac.do.rules="uid=$(id -u alice)>uid=0"
root@freebsd# sysctl security.mac.do.rules=uid="$(id -u alice)>any"
Explanation
- "uid=$(id -u alice)>uid=$(id -u bob)"
Permits useraliceto execute commands as userbob. - "gid=${gid}>uid=$(id -u bob)"
Permits any member of thebeatlesgroup to execute commands as userbob. Thegidvariable is first set by looking up thebeatlesgroup ID. - "uid=$(id -u alice)>uid=0"
Permits useraliceto execute commands as therootuser (UID 0). - "uid=$(id -u alice)>any"
Permits useraliceto execute commands as any other user on the system.
This could be risky.
Conclusion
Context
With the mac_do policy loaded and the relevant rules in place, alice can now use the mdo(1) utility to launch the weechat program as bob:
alice@freebsd$ mdo -u bob /usr/local/bin/weechat
Explanation
- mdo -u bob /usr/local/bin/weechat
Executes the weechat program as the user "bob".