What is the best way for checking if the user of a script has root-like privileges?

Question:

I have a Python script that will be doing a lot of things that would require root-level privileges, such as moving files in /etc, installing with apt-get, and so on. I currently have:

if os.geteuid() != 0:
    exit("You need to have root privileges to run this script.nPlease try again, this time using 'sudo'. Exiting.")

Is this the best way to do the check? Are there other best practices?

Asked By: Paul Hoffman

||

Answers:

os.geteuid gets the effective user id, which is exactly what you want, so I can’t think of any better way to perform such a check. The one bit that’s uncertain is that “root-like’ in the title: your code checks for exactly root, no “like” about it, and indeed I wouldn’t know what “root-like but not root” would mean — so, if you mean something different than “exactly root”, perhaps you can clarify, thanks!

Answered By: Alex Martelli

Under the EAFP (Easier to Ask Forgiveness than Permission) principle:

import errno

try:
    os.rename('/etc/foo', '/etc/bar')
except IOError as e:
    if e[0] == errno.EPERM:
       sys.exit("You need root permissions to do this, laterz!")

If you are concerned about the non-portability of os.geteuid() you probably shouldn’t be mucking with /etc anyway.

Answered By: msw

It all depends how portable you want you app to be. If you mean bussiness, the we have to assume that administrator account does not always equal 0. This means that checking for euid 0 is not enough. Problem is, there are situations where one command will behave as if you are root and next will fail with permission denied (think SELinux & co.). Therefore it’s really better to fail gracefully and check for EPERM errno whenever it’s appropriate.

Answered By: Stan

If you really want your code to be robust across a wide variety of Linux configurations I’d suggest that you consider the corner cases where someone may be using SELinux, or filesystem ACLs, or the “capabilities” features that have been in the Linux kernel since v. 2.2 or so. Your process might be running under some wrapper that has used SELinux, or some Linux capabilities library, such as libcap2 libcap-ng, or fscaps or elfcap via something more exotic like Niels Provos’ wonderful and sadly under-appreciated systrace system.

All of these are ways that you code might be running as non-root and yet your process might have been delegated the necessary access to perform its work without EUID==0.

So I’d suggest that you consider writing your code more Pythonically, by wrapping operations that may fail due to permissions or other issues with exception handling code. If you’re shelling out to perform various operations (e.g. using the subprocess module) you might offer to prefix all such calls with sudo (as a command line, environment, or .rc file option, for example). If it’s being run interactively you can offer to re-execute any commands that raise permissions related exceptions using sudo (optionally only if you find sudo on the os.environ[‘PATH’]).

Overall it’s true that most Linux and UNIX systems still have most of their administration done by a ‘root’ privileged user. However, it’s old school and we, as programmers, should try to support newer models. Trying your operations and letting the exception handling do its job allows your code to work under any system that transparently permits the operations you need, and being aware of and ready to use sudo is a nice touch (as it is, by far, the most widespread tool for controlled delegation of system privileges).

Answered By: Jim Dennis

Answer to the second part of the question

(sorry the comment box was too small)

Paul Hoffman, you are correct, I only addressed one part of your question dealing with intrinsics, but it wouldn’t be a worthy scripting language if it couldn’t handle apt-get. The preferred library is a tad verbose but it does the job:

>>> apt_get = ['/usr/bin/apt-get', 'install', 'python']
>>> p = subprocess.Popen(apt_get, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> p.wait()
100                 # Houston, we have a problem.
>>> p.stderr.read()
'E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)'
'E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?n'

But Popen is a generalized tool and can be wrapped for convenience:

$ cat apt.py
import errno
import subprocess

def get_install(package):
    cmd = '/usr/bin/apt-get install'.split()
    cmd.append(package)
    output_kw = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE}
    p = subprocess.Popen(cmd, **output_kw)
    status = p.wait()
    error = p.stderr.read().lower()
    if status and 'permission denied' in error:
        raise OSError(errno.EACCES, 'Permission denied running apt-get')
    # other conditions here as you require
$ python
>>> import apt
>>> apt.get_install('python')
Traceback ...
OSError: [Errno 13] Permission denied running apt-get

And now we’re back to exception handling. I will decline to comment on the Java-like over-generality of the subprocess module.

Answered By: msw

I like to check for sudo in the environmental variables:

if not 'SUDO_UID' in os.environ.keys():
  print "this program requires super user priv."
  sys.exit(1)
Answered By: vossman77

You can prompt the user for sudo access:

import os, subprocess

def prompt_sudo():
    ret = 0
    if os.geteuid() != 0:
        msg = "[sudo] password for %u:"
        ret = subprocess.check_call("sudo -v -p '%s'" % msg, shell=True)
    return ret

if prompt_sudo() != 0:
    # the user wasn't authenticated as a sudoer, exit?

The sudo -v switch update the user’s cached credentials (see man sudo).

Answered By: jcarballo

My app works with this code:

import os
user = os.getenv("SUDO_USER")
if user is None:
    print "This program need 'sudo'"
    exit()
Answered By: Guillaume

For linux:

def is_root():
    return os.geteuid() == 0
Answered By: Dmytro
import os

def check_privileges():

    if not os.environ.get("SUDO_UID") and os.geteuid() != 0:
        raise PermissionError("You need to run this script with sudo or as root.")

SUDO_UID is not available if script is not run with sudo.

Answered By: Thomas
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.