31

I have a program that should behave differently if it is being run under "sudo". Is there a way it can find out if it was run under sudo?

Update: Someone asked why would I want to do this. In this case, on a Mac using MacPorts there is output that tells you to cut-and-paste a particular command. If the MacPorts command was run with "sudo", it should include sudo in the sample command:

$ sudo port selfupdate 
--->  Updating MacPorts base sources using rsync
MacPorts base version 2.2.1 installed,
MacPorts base version 2.2.1 downloaded.
--->  Updating the ports tree
--->  MacPorts base is already the latest version

The ports tree has been updated. To upgrade your installed ports, you should run
  port upgrade outdated

^^^^^^^^^ it would be really sweet if it output "sudo port upgrade outdated" instead.  It would be even better if it just did it for you :-)
TomOnTime
  • 7,945
  • 6
  • 32
  • 52
  • I'm curious: can you explain how it should behave differently? – sciurus Jan 21 '14 at 00:36
  • 1
    @sciurus usually the common use-case is in an install script that requires root privileges; if you don't have them just die immediately. – Nick T Jan 21 '14 at 07:38
  • 4
    This sounds like http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem/66378#66378 . What is it you *really* want to do? – Jenny D Jan 21 '14 at 11:37
  • I vaguely remember some command being aware that it's run as root or not... I think the answer is "yes" – Rolf Jan 21 '14 at 13:22

7 Answers7

54

Yes, there are 4 environment variables set when a program is running under sudo:

$ sudo env |grep SUDO
SUDO_COMMAND=/usr/bin/env
SUDO_USER=tal
SUDO_UID=501
SUDO_GID=20

Note that these can be faked by simply setting them. Don't trust them for anything critical.

For example: In this program we need to tell the user to run some other program. If the current one was run with sudo, the other one will be too.

#!/bin/bash

echo 'Thank you for running me.'

if [[ $(id -u) == 0 ]]; then
  if [[ -z "$SUDO_COMMAND" ]]; then
    echo 'Please now run: next_command'
  else
    echo 'Please now run: sudo next_command'
  fi
else  echo 'Error: Sadly you need to run me as root.'
  exit 1
fi

Note that it only tests for a SUDO_* variable if it can first prove that it is running as root. Even then it only uses it to change some helpful text.

TomOnTime
  • 7,945
  • 6
  • 32
  • 52
  • 3
    What would I use for "anything critical?" – Kevin Jan 21 '14 at 04:39
  • 4
    @Kevin if someone screws with their environment to fake being at elevated privileges, then they hopefully know what they're doing and will accept the consequences. – Nick T Jan 21 '14 at 07:41
  • Downvoted because any answer that has to be qualified by "don't trust them for anything critical" is not an answer at all, but an ugly hack. – Stephen C Aug 26 '17 at 23:05
  • 1
    This is a perfectly acceptable answer to the question that was asked. The warning needs to be there for people that might try to _prevent_ the use of sudo, rather than just _detect_ it. Prevention should be done using sudo's command aliases to limit what commands the user can run. – dwurf Sep 15 '19 at 07:59
11

This does not answer the question directly but I do not think the right question is being asked here. It appears to me the asker wants a program which will act different presumably if it has certain permissions or not, however I would argue checking for sudo is not the way to do that. Firstly many systems may not implement a "sudo", it is by no means required on Linux or many Unixes.

For example a user may be logged in as root already, making the sudo nonsensical or perhaps the system has non root users who still have capabilities to perform the administrative task that the program may wish to do. Finally perhaps the system has no root or sudo at all and instead uses a mandatory access control system with different capabilities and no catch all superuser to sudo into. Or the user could be sudoed, but into an account that has -less- permissions than their own account for security reasons (I often run untrusted code with a temporary unprivileged user who can only write to ramdisks in order to drop, not raise my permissions). It is overall a bad idea to assume a specific permissions model like sudo or the existence of root or to assume a sudoed user has any particular privileges.

If you want to find out if you have permissions to perform an operation the best way is usually to simply try and do it then check errno for permissions issues if it fails or if it is a multi stage operation that must either all fail or all succeed you can check if an operation will work with functions like the POSIX access function (beware of possible race conditions here if permissions are actively being changed)

If in addition you need to know the real user behind the sudo you can use getlogin function which should work for any interactive session with an underlying terminal and would allow you for example to find who is 'really' running the command for auditing or find the home directory of the real user to save logs.

Finally if what you really want is to find out if a user has root access (Still a bad idea but less implementation specific) you can use getuid to check for a uid of 0 and thus root.

Burhan Ali
  • 153
  • 1
  • 3
  • 13
Vality
  • 346
  • 4
  • 13
7

There are two mechanisms that can be used.

  • Checking the environment variables can be faked either way, but is the easiest to do. growisofs doesn't like to run under SUDO, so I unset the SUDO variables in scripts where I use it. It can be faked the other way. (The SUDO variable are also carried into the environment for scripts run under the at and batch commands.)
  • Another way to see if you are running under sudo is to walk the process list from your parent process up looking for sudo. It would be difficult to hide that you were running under sudo this way, but is more complex. It is still possible to fake that you are running under sudo.

It is more common to check if you are running as the appropriate user. The id command can be used to do this. TomOnTime's script uses the id command to determine if sudo might be required to run the next command.

BillThor
  • 27,737
  • 3
  • 37
  • 69
  • 1
    *> It would be difficult to hide that you were running under sudo this way* Couldn't you simply rename the `sudo` binary to something else? Or name some other executable `sudo` to fake the opposite? Not that I'd really expect anyone sane to do that... – Bob Jan 21 '14 at 05:10
  • @Bob You would need `root` access to rename the `sudo` executable. A normal user would not be able to do this. I did note that you can fake that your are running under `sudo`. Renaming an existing program would work. The code required for a dedicated fake `sudo` program is trivial. – BillThor Jan 22 '14 at 23:43
  • you can just download the sudo binary from somewhere and give it the right permissions... – Jens Timmerman Jan 24 '14 at 14:13
  • @JensTimmerman You need root access to give it right permissions. If you can just rename it or hard link it. No need to download it. If you install it SUID to a target user id (if will work that way), you have to have already compromised the target user id. – BillThor Jan 24 '14 at 14:16
  • ah, right, it refuses to work without setuid, my bad... – Jens Timmerman Jan 24 '14 at 16:28
2

You can compare effective vs. real user id.

This does not strictly mean it is running undo sudo (could be setuid'd also), but indicates that the program has more rights than the user may expect. (for example in a program which is normally executed without such rights, but needs to be run with them during installation or to install updates. Then, you can use this to give some warning feedback about that).

blabla999
  • 129
  • 1
  • 3
    Nope, `sudo` sets both the effective and real id. As opposed to suid, which does not. Here is a trivial C program you can use to test (sorry, the comment will remove newlines, you'll have to add them back): `#include #include #include int main() { printf("real = %i, effective = %i\n", getuid(), geteuid()); return 0; }` – derobert Jan 27 '14 at 20:34
  • ... or, alternatively, `perl -E 'say $<, "\n", $>'` – derobert Jan 27 '14 at 20:35
2

You can check the effective UID (EUID) variable, as follows:

if [[ $EUID -eq 0 ]]; then
    echo "running as root"
else
    echo "not running as root"
fi
Eliran Malka
  • 131
  • 7
  • This does not answer the question. The code gives the same output when run as root as it does when run as sudo. – Stephen C Aug 26 '17 at 23:11
  • @StephenC - i think that for the purposes of the OP, there is no difference. IMHO their goal is to identify sudo execution or execution from an authentic root shell without distinction. – Eliran Malka Aug 27 '17 at 10:23
  • 1
    I think you might be right. I came here because I didn't understand why `sudo echo $USER` prints the unprivileged user name (the variable is substituted BEFORE the sudo). I actually ended up using your code in my script. Thank you! – Stephen C Aug 28 '17 at 04:11
-1

You can touch a file in /root and then if -e it. And if -e is true, rm (checking the error code) it so your test works next time.

Checking the error code (or return code) after the rm prevents someone from handily using sudo powers to create the file to play a prank on you.

Krista K
  • 518
  • 7
  • 21
  • 4
    This answer checks if the process has permission to write to `/root` (which is not necessarily the same as running as UID 0), but does not check if it gained those permissions using `sudo`. – Ladadadada Jan 21 '14 at 12:16
  • Yes, the "thousand ways to skin a cat"; as I wrote the answer, I thought of another 4 or 5 things, so I wrapped it up. – Krista K Jan 22 '14 at 00:44
-2

I found that this works well for this

[ $(cat /proc/$PPID/loginuid) -ne 0 ] && [ "$USER" == "root" ]
ruckc
  • 131
  • 3
  • This doesn't work when I try it on FreeBSD, Centos7, or MacOS X. I think you mean "/proc" instead of "/etc". But even with that change, it doesn't work on any of those three. "$USER" is reset by sudo. Did you mean $SUDO_USER? Also, did you mean "[[" instead of "["? – TomOnTime Oct 01 '15 at 19:30
  • 1
    i fixed the example. basically checking for the loginuid is not equal to 0, but the user is root. – ruckc Nov 11 '17 at 03:09