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

PythonRootPrivileges

Python Problem Overview


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?

Python Solutions


Solution 1 - Python

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!

Solution 2 - Python

Under the "Easier to Ask Forgiveness than Permission" principle:

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.

Solution 3 - Python

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).

Solution 4 - Python

For linux:

def is_root():
    return os.geteuid() == 0

Solution 5 - Python

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)

Solution 6 - Python

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).

Solution 7 - Python

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.

Solution 8 - Python

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.

Solution 9 - Python

My app works with this code:

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

Solution 10 - Python

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPaul HoffmanView Question on Stackoverflow
Solution 1 - PythonAlex MartelliView Answer on Stackoverflow
Solution 2 - PythonmswView Answer on Stackoverflow
Solution 3 - PythonjcarballoView Answer on Stackoverflow
Solution 4 - PythonDmytroView Answer on Stackoverflow
Solution 5 - Pythonvossman77View Answer on Stackoverflow
Solution 6 - PythonJim DennisView Answer on Stackoverflow
Solution 7 - PythonThomasView Answer on Stackoverflow
Solution 8 - PythonmswView Answer on Stackoverflow
Solution 9 - PythonGuillaumeView Answer on Stackoverflow
Solution 10 - PythonStanView Answer on Stackoverflow