If you take a look at the executable sudo
:
$ which sudo
/usr/bin/sudo
$ ls -la /usr/bin/sudo
---s--x--x 2 root root 208808 Jun 3 2011 /usr/bin/sudo
You'll notice that it carries the permission bits ---s--x--x
. These can be broken down as follows:
-|--s|--x|--x
- - first dash denotes if a directory or a file ("d" = dir, "-" = file)
--s - only the setuid bit is enabled for user who owns file
--x - only the group execute bit is enabled
--x - only the other execute bit is enabled
So when a program has it's setuid bit enabled (also referred to as SUID) it means that when someone runs this program it will run with the credentials of the user that owns the file, aka. root in this case.
Example
If I run the following command as user saml:
$ whoami
saml
$ sudo su -
[sudo] password for saml:
You'll notice that the execution of sudo
actually is running as root:
$ ps -eaf|grep sudo
root 20399 2353 0 05:07 pts/13 00:00:00 sudo su -
setuid mechanism
If you're curious how SUID works take a look at man setuid
. Here's an excerpt from the man page that explains it better than I could:
setuid() sets the effective user ID of the calling process. If the
effective UID of the caller is root, the real UID and saved
set-user-ID are also set. Under Linux, setuid() is implemented like
the POSIX version with the _POSIX_SAVED_IDS feature. This allows a
set-user-ID (other than root) program to drop all of its user
privileges, do some un-privileged work, and then reengage the original
effective user ID in a secure manner.
If the user is root or the program is set-user-ID-root, special care
must be taken. The setuid() function checks the effective user ID of
the caller and if it is the superuser, all process-related user ID's
are set to uid. After this has occurred, it is impossible for the
program to regain root privileges.
The key concept here is that programs have a real userid (UID) and an effective one (EUID). Setuid is setting the effective userid (EUID) when this bit is enabled.
So from the kernel's perspective it's known that in our example, saml
is still the original owner (UID), but the EUID has been set with whomever is the owner of the executable.
setgid
I should also mention that when we're breaking down the permissions on the sudo command the second group of bits were for group permissions. The group bits also has something similar to setuid called set group id (aka. setgid, SGID). This does the same thing as SUID except it runs the process with the group credentials instead of the owner credentials.
References
Then answer is that sudo
has a bug. First, the workaround: I put this in my /etc/sudoers.d/zabbix file
:
zabbix ALL=(root) NOPASSWD: /bin/env SHELL=/bin/sh /usr/local/bin/zabbix_raid_discovery
and now subcommands called from zabbix_raid_discovery
work.
A patch to fix this will be in sudo 1.8.15. From the maintainer, Todd Miller:
This is just a case of "it's always been like that". There's not
really a good reason for it. The diff below should make the behavior
match the documentation.
- todd
diff -r adb927ad5e86 plugins/sudoers/env.c
--- a/plugins/sudoers/env.c Tue Oct 06 09:33:27 2015 -0600
+++ b/plugins/sudoers/env.c Tue Oct 06 10:04:03 2015 -0600
@@ -939,8 +939,6 @@
CHECK_SETENV2("USERNAME", runas_pw->pw_name,
ISSET(didvar, DID_USERNAME), true);
} else {
- if (!ISSET(didvar, DID_SHELL))
- CHECK_SETENV2("SHELL", sudo_user.pw->pw_shell, false, true);
/* We will set LOGNAME later in the def_set_logname case. */
if (!def_set_logname) {
if (!ISSET(didvar, DID_LOGNAME))
@@ -984,6 +982,8 @@
if (!env_should_delete(*ep)) {
if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
ps1 = *ep + 5;
+ else if (strncmp(*ep, "SHELL=", 6) == 0)
+ SET(didvar, DID_SHELL);
else if (strncmp(*ep, "PATH=", 5) == 0)
SET(didvar, DID_PATH);
else if (strncmp(*ep, "TERM=", 5) == 0)
@@ -1039,7 +1039,9 @@
if (reset_home)
CHECK_SETENV2("HOME", runas_pw->pw_dir, true, true);
- /* Provide default values for $TERM and $PATH if they are not set. */
+ /* Provide default values for $SHELL, $TERM and $PATH if not set. */
+ if (!ISSET(didvar, DID_SHELL))
+ CHECK_SETENV2("SHELL", runas_pw->pw_shell, false, false);
if (!ISSET(didvar, DID_TERM))
CHECK_PUTENV("TERM=unknown", false, false);
if (!ISSET(didvar, DID_PATH))
Best Answer
When searching trough the source code of
login
, we see this passage: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 invokelogin
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.sudo
itself has the setuid bit set, thus euid and ruid differ. But, sudo executes the binary oflogin
and sets both the real and the effective uid to the target user. It will therefore not fail anymore.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).