Change to sudo user within a python script - python

I have a problem. I am writing a piece of software, which is required to perform an operation which requires the user to be in sudo mode. running 'sudo python filename.py' isn't an option, which leads me to my question. Is there a way of changing to sudo half way through a python script, security isn't an issue as the user will know the sudo password the program should run in the following way to illustrate the issue
program running as normal user
...... performing operations
user enters sudo password
user changed to sudo
sub program requiring sudo permission is run
on trigger even (end of sub program) user becomes normal user again
...... performing operations
My problem lies in step 3, any pointers or frameworks you could suggest would be of great help.
Cheers
Chris

It is better to run as little of the program as possible with elevated privileges. You can run the small part that needs more privilege via the subprocess.call() function, e.g.
import subprocess
returncode = subprocess.call(["/usr/bin/sudo", "/usr/bin/id"])

Don't try and make yourself sudo just check if you are and error if your not
class NotSudo(Exception):
pass
if os.getuid() != 0:
raise NotSudo("This program is not run as sudo or elevated this it will not work")

I've recently dealt with this problem while making a system installation script. To switch to superuser permissions, I used subprocess.call() with 'sudo':
#!/usr/bin/python
import subprocess
import shlex
import getpass
print "This script was called by: " + getpass.getuser()
print "Now do something as 'root'..."
subprocess.call(shlex.split('sudo id -nu'))
print "Now switch back to the calling user: " + getpass.getuser()
Note that you need to use shlex.split() to make your command usable for subprocess.call(). If you want to use the output from a command, you can use subprocess.check_output(). There is also a package called 'sh' (http://amoffat.github.com/sh/) that you can use for this purpose.

Use Tcl and Expect, plus subprocess to elevate yourself. So basically it's like this:
sudo.tcl
spawn sudo
expect {
"Password:" {
send "password"
}
}
sudo.py
import subprocess
subprocess.call(['tclsh', 'sudo.tcl'])
And then run sudo.py.

If you are able to encapsulate just the necessary functionality requiring elevated privileges in a separate executable, you could use the setuid bit on the executable program, and call it from your user-level python script.
In this way, only the activity in the setuid-executable run as root, however executing this does NOT require sudo, i.e., root privileges. Only creating/modifying the setuid-executable requires sudo.
There are a few security implications, such as ensuring that your setuid executable program properly sanitizes any user input (e.g., parameters), so that it cannot be tricked into doing something it should not (confused deputy problem).
ref:
http://en.wikipedia.org/wiki/Setuid#setuid_on_executables
edit: setuid only seems to work for compiled executables (binaries), and not interpreted scripts, so you may need to use a compiled setuid wrapper.

You can use setuid to set the users uid. But for obvious security reasons you can only do this if you are root (or the program has suid root rights). Both of these are probably a bad idea.
In this case you need to sudo rights to run a specific program. In that case just sub to "sudo theprogram" instead.

import subprocess
subprocess.check_output("sudo -i -u " + str(username) + " ls -l", shell=True).decode("utf-8").strip()

Not sure how this would help you, and it does not answer the question, yet, it is a workaround to think about when you run into a needed "root" user problem and you need to be "root" only to read / write in a folder or file.
You can then change the permissions and also switch them back afterwards. I had this in a docker-compose file that started a Python script that deployed an application to a server. This workaround was the only way how I got it to run. I do not even need to change the permissions from the container bash, instead, the script does that, and only the password is needed twice.
Before this workaround, I tried to change to the root user and then execute large blocks of code with that root user, to no avail.
run("ls -ld /usr/local/my_project/")
run("sudo chmod o+wx /usr/local/my_project/")
run("ls -ld /usr/local/my_project/")
my_code_that_needed_root_rights_and_now_runs_without_root_user()
run("sudo chmod 774 /usr/local/my_project/")
run("ls -ld /usr/local/my_project/")
And the output:
[server_connection] run: ls -ld /usr/local/my_project/
[server_connection] Login password for 'my_user':
[server_connection] out: drwxrwxr-- 45 root 100005 4096 Apr 25 13:52 /usr/local/my_project/
[server_connection] out:
[server_connection] run: sudo chmod o+wx /usr/local/my_project/
[server_connection] out: [sudo] password for my_user:
[server_connection] out:
[server_connection] run: ls -ld /usr/local/my_project/
[server_connection] out: drwxrwxrwx 45 root 100005 4096 Apr 25 13:52 /usr/local/my_project/
[server_connection] out:
[...]
[server_connection] run: sudo chmod 774 /usr/local/my_project/
[server_connection] out: [sudo] password for my_user:
[server_connection] out:
[server_connection] run: ls -ld /usr/local/my_project/
[server_connection] out: drwxrwxr-- 46 root 100005 4096 Apr 25 14:02 /usr/local/my_project/
[server_connection] out:
After this, the server folder had the same permissions as before, and the code did not need the root user to run through.

Are you talking about having the user input password half way through your execution? raw_input() can take a user input from console, but it will not mask the password.
>>>> y = raw_input()
somehting
>>> y
'somehting'

Related

How do I make a root-only shell command executable by normal users

I need to run the /sys/bus/usb/drivers/bind and its companion /sys/bus/usb/drivers/unbind from a Python script WITHOUT touching the sudoers file.
The main issues is that I can't use sudo from the Python script, so I need to make bind/unbind runnable by the normal user (I can't run the Python script as root either).
Update: I created a group, added the normal user and root to that group and did
chmod root:mygroup
The commands I want to access without having to use sudo or provide a password are:
/sys/bus/usb/drivers/usb/bind /sys/bus/usb/drivers/usb/unbind
They have the following permissions:
-rwxrwxrwx 1 root mygroup # (for bind)
-rwxrwxrwx 1 root mygroup # (for unbind)
Also, the folders leading up to the two programs/commands (namely /sys/bus/usb/drivers/usb/) have this:
drwxr-xr-x 2 root root
I still get "permission denied" but if I **keep all configurations plus a chmod 777** on the bind and unbind files it works, where is my error?

script does not switch to another user

I am working on a script that at a certain point needs to switch to the root user (executing "sudo rootsh" is the only accepted way to switch to root on our servers,) after which it will execute a certain command.
I am not sure what I am missing, but the script simply ignores the part when it should switch to root and continues executing the commands with the user that started the script.
If you check the generated whoami.txt file, you will notice that the user is not root. Please keep in mind that the user executing the script can switch to root without any issue while executing the sudo rootsh command.
Here is the code I am using:
import subprocess
def switch_user():
commands = '''
sudo rootsh
whoami > whoami.txt
sysctl -a | grep kernel.msgmni'''
process = subprocess.Popen('/bin/bash', stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
out, err = process.communicate(commands.encode('utf-8'))
switch_user()
Any idea what I am doing wrong? Thanks.
Instead of Popening a subprocess to run bash, and from that opening a separate privileged shell, Popen the command sudo rootsh directly. If that succeeds (requires that the user be permitted to sudo rootsh without providing a password) then deliver the rest of the commands by communicating with the subprocess.
That would be something along these lines:
import subprocess
def switch_user():
# These shell commands will be used as input to the root shell
commands = '''whoami > whoami.txt
sysctl -a | grep kernel.msgmni'''
# Launch the root shell
process = subprocess.Popen('/usr/bin/sudo rootsh',
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
# Send the shell's input to it and receive back its output
out, err = process.communicate(commands.encode('utf-8'))
switch_user()
You may need to modify that for your purposes. In particular, if your sudo command lives at a different location then you may need to modify the path to it. And I emphasize again that this approach depends on being able to obtain a root shell without providing a password. Sudo can be configured that way, but it is not the default.
I finally managed to make this work after doing a more thorough investigation with the guys from the OS team. I'll post this, maybe it would be useful for somebody in the future:
import os
os.system("sudo rootsh -i -u root 'sysctl -a | grep kernel.msgmni' > parameter_value.txt")
The key was to insert the -i and -u options:
-i [command]
The -i (simulate initial login) option runs the shell specified by the password database entry of the target user as a login
shell.
This means that login-specific resource files such as .profile or .login will be read by the shell. If a command is
specified, it is
passed to the shell for execution via the shell's -c option. If no command is specified, an interactive shell is executed.
sudo
attempts to change to that user's home directory before running the shell. The security policy shall initialize the
environment to a
minimal set of variables, similar to what is present when a user logs in. The Command Environment section in the
sudoers(5) manual documents how the -i option affects the environment in which a command is run when the sudoers policy is in use.
-u user
The -u (user) option causes sudo to run the specified command as a user other than root. To specify a uid instead
of a user name, #uid.
When running commands as a uid, many shells require that the # be escaped with a backslash ('\'). Security policies may
restrict uids
to those listed in the password database. The sudoers policy allows uids that are not in the password database as
long as the targetpw
option is not set. Other security policies may not support this.
Thank you all for your answers :)

Python subprocess permission

When a command is executed via subprocess.run, what permissions is the command executed with? Is it the permissions of the enclosing python file? Is it user permissions?
For example, if a python file were run with sudo:
sudo python3 file.py
and file.py contained the line
subprocess.run([ 'chmod', '+x', 'file.sh' ])
Would chmod be run with super user permissions?
Intuitively, it should, because, through sudo, you are giving the script itself superuser permissions - this means that the commands it runs would be effectively performed by the superuser.
However, to verify, let's do a little experiment.
test.py
import subprocess
subprocess.run('whoami')
(Now, on the terminal)
$ sudo python test.py
root
$
So, chmod will run with superuser permissions, yes.

Denied persmission to .py file on ev3dev, PuTTY

I have EV3 Lego Mindstorms and I instaled on it ev3dev operating system. I set the connection with the PC via SSH and using PuTTY I started to "programming". I used the cat > test2.py and wrote this code:
#!/usr/bin/env python3
import ev3dev.ev3 as ev3
motor = ev3.LargeMotor('outA')
motor.run_timed(time_sp = 1000, speed_sp = 500)
I saved the file and initialized it using ./test2.py. I got this output:
-bash: ./test2.py: Persmission denied
What caused it and what should I change?
try this:
sudo python3 test2.py
that will allows you to open almost anything in linux
Use ls -la ./test2.py in order to see the file permissions.
Look at the beginning of the output, you'll see something like this:
-rw-rw-r--
The first - means if is a directory or a file. In this case means that is a file.
Now If you observe the remaining chars there are 3 sets of 3 chars with means the permissions for the owner of the file, the owner group and the last set is for the rest of the users.
We have permissions to read, write and execute and in the example I showed there are read and write permissions for the owner user and the owner group but non permissions for the other users.
As Is said above you can just use sudo every time you execute the script but to run it with root privileges. However I would recommend you change your file permissions and using chmod
sudo chmod +x ./test2.py
This will let you execute the script. Take a look at chmod documentation to learn more: https://help.ubuntu.com/community/FilePermissions

How to invoke sudo password request in Python

I've got a USB GPIO electronic gizmo attached to a desktop PC running Linux Mint 17 "Mate"; in this environment the gizmo appears as /dev/ttyACM0. I've written a GUI Python 2.7/Tkinter program to control the gizmo via the pySerial module. The program works when run from the console using sudo.
Being a GUI program, I want to be able to run it from the "Mate" desktop - but I can't, because being a serial device, accessing the gizmo requires root privileges obtained via sudo, wot has to be invoked at a Terminal.
# here's the offending code
import serial
numa = serial.Serial("/dev/ttyACM0", 19200, timeout=1)
....
How do I invoke the "Enter your password..." routine from within the Python program so a raw user doesn't have to open a Terminal to enter the password?
Thanks for any advice you can provide!
I can't answer your question, but instead I'm going to solve your problem.
When you list the device file, you'll see something like this:
$ ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 188, 0 Apr 4 11:22 /dev/ttyACM0
Both the owner (root) and the owner group (dialout) have read-write-access (rw-), while everybody else isn't able to access the device (---). Therefore, instead of giving the program root access to your system, you can simply add the user(s) to the dialout group:
$ sudo usermod -aG dialout <username>
Logging out and back in will be necessary, but afterwards your script will be able to both read and write to the serial interface without the need of a root password.
Use gksudo rather than sudo
from subprocess import call
call('gksudo -D "Program requires root priveledge \nSudo "your command here",shell=True)
for example:
call('gksudo -D "Override Sudo message " cp /etc/hosts /home/new.hosts',shell=True)

Categories

Resources