About
This post takes a quick 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. In that sense, it is similar to the doas(1) program from OpenBSD. At the same time, mdo(1) has noticeable differences when compared with doas. Let's take a deeper look.
Background
Traditionally – UNIX programs that let one user execute a command as another user (examples: doas(1), sudo(8)) were 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 to drop privileges to the "target" user.
This changes the real|effective user ID along with the real|effective
group ID associated with the process. Finally, execvpe
(another system call) is executed, and a new process starts with the
privileges of the "target" user. That's the pattern that tools like
doas(1) and sudo(8) have always followed.
But mdo(1) is different. It does not depend on a setuid binary. 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. In that sense, mdo(1) is pushing the envelope with a new, innovative approach.
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 useralice
to execute commands as userbob
. - "gid=${gid}>uid=$(id -u bob)"
Permits any member of thebeatles
group to execute commands as userbob
. Thegid
variable is first set by looking up thebeatles
group ID. - "uid=$(id -u alice)>uid=0"
Permits useralice
to execute commands as theroot
user (UID 0). - "uid=$(id -u alice)>any"
Permits useralice
to 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".