Shell – How does sudo work (besides setuid)

setuidshell-scriptsudo

sudo is elevated to root upon launch because it's binary has a setuid enabled. But when I give /bin/login the exact privileges sudo has, and I try to run it from a user that isn't root, I get the error:

No utmp entry.  You must exec "login" from the lowest level "sh"

My first question is, what does that error mean? My second is, why does it work when I run sudo login from that same user? Besides using a setuuid to elevate to root, how else does sudo work? I've written a simple dash script at ~/root to run whoami and exit, but it returns the user name of my unprivileged user.

$ ls -l ./root /usr/bin/sudo
-rwsr-xr-x 1 root root     29 Jul 15 02:40 ./root
-rwsr-xr-x 1 root root 157760 Jan 10  2016 /usr/bin/sudo

$ ./root
unprivileged user

So I have three questions:

  1. What is the error I get with login?
  2. Why does login work with sudo?
  3. Why my simple script to run whoami doesn't work?

Best Answer

What is the error I get with login?

When searching trough the source code of login, we see this passage:

amroot = (getuid () == 0);
[...]
utent = get_current_utmp ();
/*
 * Be picky if run by normal users (possible if installed setuid
 * root), but not if run by root. This way it still allows logins
 * even if your getty is broken, or if something corrupts utmp,
 * but users must "exec login" which will use the existing utmp
 * entry (will not overwrite remote hostname).  --marekm
 */
if (!amroot && (NULL == utent)) {
     (void) puts (_("No utmp entry.  You must exec \"login\" from the lowest level \"sh\""));
     exit (1);
}

The interessting part is the if clause at the end. The error message appears when getuid() is not 0 and if there is no utmp entry. getuid() returns the real user ID of the calling process. If you invoke login with the setuid flag set, the real uid and effective uid differ. login is, as the comment in the source snippet suggest, "picky" in such cases: it fails and throws the mentioned error.


Why does login work with sudo?

sudo itself has the setuid bit set, thus euid and ruid differ. But, sudo executes the binary of login and sets both the real and the effective uid to the target user. It will therefore not fail anymore.


Why my simple script to run whoami doesn't work?

Your script has a shebang line (#!) in it (I assume). What happens is the following: the kernel opens the executable (in your case a text file) and observes a #!. Instead of the text file, the interpreter given in the shebang line is invoked (for example /bin/bash or /bin/sh) with the scripts name as first argument. Most unix and linux operating systems ignore the setuid bit when set on a script, because it is a security issue.

Imagine a script with setuid set. The attacker can then create a symbolic link to this script, invoke that script, and then change the symbolic link to his script, right before the interpreter opens the script behind the symbolic link (which is the evil one then).

Related Question