Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to elevate to root within a python script?

I am trying to write a python script which will ask for root permissions. I have tried using elevate for python (which supposedly shows a dialog box asking for a root or "admin" password), but it gave a "No such file or directory" error when I ran it.

import os
from elevate import elevate

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

print("before ", is_root())
elevate()
print("after ", is_root())

Why does this happen? Are there any working alternatives that utilize the system GUI?
EDIT: I am using Kubuntu 18 with Python 3.6, but I would like for it to work on Windows too. FURTHER EDIT: This is a question about dialog boxes (for basic/Windows users), not TUI.

like image 661
nerdguy Avatar asked Apr 25 '19 21:04

nerdguy


People also ask

How do I access root in Python?

sqrt() function is an inbuilt function in Python programming language that returns the square root of any number. Syntax: math. sqrt(x) Parameter: x is any number such that x>=0 Returns: It returns the square root of the number passed in the parameter.

How do you increase privilege in Python?

You can make a shortcut somewhere and as the target use: python yourscript.py then under properties and advanced select run as administrator. When the user executes the shortcut it will ask them to elevate the application.

How do I run a sudo script in Python?

From the terminal instead of doing python yourProgram.py , do sudo python yourProgram.py . It will ask for your password so type it and it should run.


Video Answer


2 Answers

Here's a full solution that works under Windows and Linux to elevate UAC or sudo to root, working whether the code is interpreted, frozen or compiled with Nuitka.

[EDIT] Disclaimer: The code in this post is outdated. I have published the elevation code as a python package. Install with pip install command_runner

Usage:

from command_runner.elevate import elevate

def main():
    """My main function that should be elevated"""
    print("Who's the administrator, now ?")

if __name__ == '__main__':
    elevate(main)

[/EDIT]

The code basically checks if it's already elevated, if yes, runs main() function, if not, it spawns an elevated subprocess which will run main() function.

Hope it helps

import os
import sys
import subprocess
import logging

if os.name == 'nt':
    try:
        import ctypes
        # import ctypes  # In order to perform UAC check with ctypes.windll.shell32.ShellExecuteW()
        import win32event  # monitor process
        import win32process  # monitor process
        from win32com.shell.shell import ShellExecuteEx
        from win32com.shell import shellcon
    except ImportError:
        raise ImportError('Cannot import ctypes for checking admin privileges on Windows platform.')


logger = logging.getLogger(__name__)


def main():
    # Your code goes here
    # Keep in mind that elevating under windows will not redirect stdout/stderr to the main process
    print('Hello, elevated world !')


def is_admin():
    """
    Checks whether current program has administrative privileges in OS
    Works with Windows XP SP2+ and most Unixes

    :return: Boolean, True if admin privileges present
    """
    current_os_name = os.name

    # Works with XP SP2 +
    if current_os_name == 'nt':
        try:
            return ctypes.windll.shell32.IsUserAnAdmin() == 1
        except Exception:
            raise EnvironmentError('Cannot check admin privileges')
    elif current_os_name == 'posix':
        # Check for root on Posix
        return os.getuid() == 0
    else:
        raise EnvironmentError('OS does not seem to be supported for admin check. OS: %s' % current_os_name)


# Improved answer I have done in https://stackoverflow.com/a/49759083/2635443
if __name__ == '__main__':
    if is_admin():
        main(sys.argv)
    else:
        # UAC elevation / sudo code working for CPython, Nuitka >= 0.6.2, PyInstaller, PyExe, CxFreeze

        # Regardless of the runner (CPython, Nuitka or frozen CPython), sys.argv[0] is the relative path to script,
        # sys.argv[1] are the arguments
        # The only exception being CPython on Windows where sys.argv[0] contains absolute path to script
        # Regarless of OS, sys.executable will contain full path to python binary for CPython and Nuitka,
        # and full path to frozen executable on frozen CPython

        # Recapitulative table create with
        # (CentOS 7x64 / Python 3.4 / Nuitka 0.6.1 / PyInstaller 3.4) and
        # (Windows 10 x64 / Python 3.7x32 / Nuitka 0.6.2.10 / PyInstaller 3.4)
        # --------------------------------------------------------------------------------------------------------------
        # | OS  | Variable       | CPython                       | Nuitka               | PyInstaller                  |
        # |------------------------------------------------------------------------------------------------------------|
        # | Lin | argv           | ['./script.py', '-h']         | ['./test', '-h']     | ['./test.py', -h']           |
        # | Lin | sys.executable | /usr/bin/python3.4            | /usr/bin/python3.4   | /absolute/path/to/test       |
        # | Win | argv           | ['C:\\Python\\test.py', '-h'] | ['test', '-h']       | ['test', '-h']               |
        # | Win | sys.executable | C:\Python\python.exe          | C:\Python\Python.exe | C:\absolute\path\to\test.exe |
        # --------------------------------------------------------------------------------------------------------------

        # Nuitka 0.6.2 and newer define builtin __nuitka_binary_dir
        # Nuitka does not set the frozen attribute on sys
        # Nuitka < 0.6.2 can be detected in sloppy ways, ie if not sys.argv[0].endswith('.py') or len(sys.path) < 3
        # Let's assume this will only be compiled with newer nuitka, and remove sloppy detections
        try:
            # Actual if statement not needed, but keeps code inspectors more happy
            if __nuitka_binary_dir is not None:
                is_nuitka_compiled = True
        except NameError:
            is_nuitka_compiled = False

        if is_nuitka_compiled:
            # On nuitka, sys.executable is the python binary, even if it does not exist in standalone,
            # so we need to fill runner with sys.argv[0] absolute path
            runner = os.path.abspath(sys.argv[0])
            arguments = sys.argv[1:]
            # current_dir = os.path.dirname(runner)

            logger.debug('Running elevator as Nuitka with runner [%s]' % runner)
            logger.debug('Arguments are %s' % arguments)

        # If a freezer is used (PyInstaller, cx_freeze, py2exe)
        elif getattr(sys, "frozen", False):
            runner = os.path.abspath(sys.executable)
            arguments = sys.argv[1:]
            # current_dir = os.path.dirname(runner)

            logger.debug('Running elevator as Frozen with runner [%s]' % runner)
            logger.debug('Arguments are %s' % arguments)

        # If standard interpreter CPython is used
        else:
            runner = os.path.abspath(sys.executable)
            arguments = [os.path.abspath(sys.argv[0])] + sys.argv[1:]
            # current_dir = os.path.abspath(sys.argv[0])

            logger.debug('Running elevator as CPython with runner [%s]' % runner)
            logger.debug('Arguments are %s' % arguments)

        if os.name == 'nt':
            # Re-run the program with admin rights
            # Join arguments and double quote each argument in order to prevent space separation
            arguments = ' '.join('"' + arg + '"' for arg in arguments)
            try:
                # Old method using ctypes which does not wait for executable to exit nor deos get exit code
                # See https://learn.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutew
                # int 0 means SH_HIDE window, 1 is SW_SHOWNORMAL
                # needs the following imports
                # import ctypes

                # ctypes.windll.shell32.ShellExecuteW(None, 'runas', runner, arguments, None, 0)

                # Metthod with exit code that waits for executable to exit, needs the following imports
                # import ctypes  # In order to perform UAC check
                # import win32event  # monitor process
                # import win32process  # monitor process
                # from win32com.shell.shell import ShellExecuteEx
                # from win32com.shell import shellcon

                childProcess = ShellExecuteEx(nShow=0, fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
                                              lpVerb='runas', lpFile=runner, lpParameters=arguments)
                procHandle = childProcess['hProcess']
                obj = win32event.WaitForSingleObject(procHandle, -1)
                exit_code = win32process.GetExitCodeProcess(procHandle)
                logger.debug('Child exited with code: %s' % exit_code)
                sys.exit(exit_code)

            except Exception as e:
                logger.info(e)
                logger.debug('Trace', exc_info=True)
                sys.exit(255)
        # Linux runner and hopefully Unixes
        else:
            # Search for sudo executable in order to avoid using shell=True with subprocess
            sudo_path = None
            for path in os.environ.get('PATH', '').split(':'):
                if os.path.isfile(os.path.join(path, 'sudo')):
                    sudo_path = os.path.join(path, 'sudo')
            if sudo_path is None:
                logger.error('Cannot find sudo executable. Cannot elevate privileges. Trying to run wihtout.')
                main(sys.argv)
            else:
                command = 'sudo "%s"%s%s' % (
                    runner,
                    (' ' if len(arguments) > 0 else ''),
                    ' '.join('"%s"' % argument for argument in arguments)
                )
                try:
                    output = subprocess.check_output(command, stderr=subprocess.STDOUT,
                                                     shell=False, universal_newlines=False)
                    output = output.decode('unicode_escape', errors='ignore')

                    logger.info('Child output: %s' % output)
                    sys.exit(0)
                except subprocess.CalledProcessError as exc:
                    exit_code = exc.returncode
                    logger.error('Child exited with code: %s' % exit_code)
                    try:
                        output = exc.output.decode('unicode_escape', errors='ignore')
                        logger.error('Child outout: %s' % output)
                    except Exception as exc:
                        logger.debug(exc, exc_info=True)
                    sys.exit(exit_code)
like image 101
Orsiris de Jong Avatar answered Oct 20 '22 00:10

Orsiris de Jong


Since your working on Linux, graphical prompts are tried before sudo by default. You should therefore disable the graphical prompts by using the following command:

elevate(graphical=False)

This Solution worked for me and is quite simple.

Source: https://pypi.org/project/elevate/

like image 39
Manumerous Avatar answered Oct 20 '22 01:10

Manumerous