Open a file as superuser in python - python

I have to open a system file and read from it. This file is usually only readable by root (the super user). I have a way to ask the user for the superuser password. I would like to use this credentials to open the file and read from it without having my entire program running as a superuser process. Is there a way to achieve this in a multiplatform way?

Since privileges work completely differently on Unix-like systems and Windows, you're going to need to have platform-specific code. In any case, you'll need to break up your program into two separate programs, one of which runs with elevated permissions and the other of which runs with standard/reduced permissions.
In Unix-like systems (including Linux and Mac OS X), the executable that runs with elevated permissions should do this:
Assume you're running as root and open the file for reading. Since you mentioned that the file is very large, you don't actually read the whole file in, you just keep an open file descriptor. If opening it fails, print an error message and exit.
Use setreuid(2) and setregid(2) to set your user ID and group ID back to an unprivileged user.
Use one of the exec(3) functions to execute the unprivileged executable.
If you want to make it so that you can run this program without using sudo, then make it owned by root and make it a set-user-ID executable with chown root the-program; chmod +s the-program.
The unprivileged program will now be run with normal permissions, but when it starts up, it will have an open file descriptor (file descriptor #3) that can be used to read from your special file.
For Windows, it's similar but slightly different:
Assume you're running as root and open the file for reading using CreateFile. Do not use default security attributes -- create a SECURITY_ATTRIBUTES structure with bInheritHandle set to TRUE so that the handle will be inherited by child processes. If opening the file failed, print an error message and exit.
Use CreateProcess to launch your child process. Pass in the handle above on the command line (e.g. printed as a numerical value); you could also use a shared memory region, but that's more trouble than it's worth for this problem.
Embed a manifest in this executable with requireAdministrator set to true. After you do this, when you run the program, you'll get a UAC prompt asking you if you want to allow the program to makes changes.
The child process then does grabs the inherited handle by parsing the command line, and it can then read in the data as it pleases.
One problem with this approach is that when you inherit a handle, you have to use the low-level system calls (read(2) on Unix, ReadFile on Windows) to read from it -- you can't use higher-level functions like C's fread(3) or C++'s iostreams (ok, Unix has fdopen(3), but there's no equivalent on Windows as far as I'm aware).
As I'm sure you've noticed by now, everything above has been in C. In Unix, this translates pretty straightforwardly into Python, since the os module has lots of goodies like setreuid, exec*, and fdopen. On Windows, you might be able to do some of this stuff with the ctypes module and/or Pywin32, but it's probably easier to stick with C.

What you're looking for is called privilege escalation, and it very much depends on the platform you're running on. In general, what your program would have to do is run a portion as the superuser. On unix systems, for instance, you might be able to use sudo to read the contents of the file.
But as mentioned, this really depends on what system you're running on.

I would split the program in two.
Handles opening the file and accessing the contents. It can assume it's started with the privileges it needs.
Everything else that doesn't require special privileges.
Put a config entry which describes how to exec or subprocess the command that requires extra privileges. ie.
access_special_file: sudo access_special_file
or
access_special_file: runas /user:AccountWithPrivs access_special_file
This offloads some of the system specifics for privilege escalation to the system shell where there may be more convenient ways of gaining the permissions you need.

On linux it's a cynch, as #ViktorKerkez showed. This is how I streamed my WiFi passwords files (readable only by root/sudo):
import subprocess
import sys
# substitute your Windoze/DOS/PowerlessShell command here:
cat_wifi_pws = 'sudo cat /etc/NetworkManager/system-connections/*'
process = subprocess.Popen(cat_wifi_pws, stdout=subprocess.PIPE, shell=True)
# read one line at a time, as it becomes available
for line in iter(process.stdout.readline, ''):
sys.stdout.write(line)
Of course this will prompt you for your sudo password. And you can use gksudo if you're on a system that has it and you prefer dialog boxes. As a bonus, if you have a decent timeout_default in /etc/sudoers, and you've recently run sudo in the same shell where you launched the python interpreter, you won't have to enter a password at all.

Related

Debugging fails when reading input from file [duplicate]

I am developing FUSE filesystem with python. The problem is that after mounting a filesystem I have no access to stdin/stdout/stderr from my fuse script. I don't see anything, even tracebacks. I am trying to launch pdb like this:
import pdb
pdb.Pdb(None, open('pdb.in', 'r'), open('pdb.out', 'w')).set_trace()
All works fine but very inconvenient. I want to make pdb.in and pdb.out as fifo files but don't know how to connect it correctly. Ideally I want to type commands and see output in one terminal, but will be happy even with two terminals (in one put commands and see output in another). Questions:
1) Is it better/other way to run pdb without stdin/stdout?
2) How can I redirect stdin to pdb.in fifo (All what I type must go to pdb.in)? How can I redirect pdb.out to stdout (I had strange errors with "cat pdb.out" but maybe I don't understand something)
Ok. Exactly what I want, has been done in http://pypi.python.org/pypi/rpdb/0.1.1 .
Before starting the python app
mkfifo pdb.in
mkfifo pdb.out
Then when pdb is called you can interact with it using these two cat commands, one running in the background
cat pdb.out & cat > pdb.in
Note the readline support does not work (i.e. up arrow)
I just ran into a similar issue in a much simpler use-case:
debug a simple Python program running from the command line that had a file piped into sys.stdin, meaning, no way to use the console for pdb.
I ended up solving it by using wdb.
Quick rundown for my use-case. In the shell, install both the wdb server and the wdb client:
pip install wdb.server wdb
Now launch the wdb server with:
wdb.server.py
Now you can navigate to localhost:1984 with your browser and see an interface listing all Python programs running. The wdb project page above has instructions on what you can do if you want to debug any of these running programs.
As for a program under your control, you can you can debug it from the start with:
wdb myscript.py --script=args < and/stdin/redirection
Or, in your code, you can do:
import wdb; wdb.set_trace()
This will pop up an interface in your browser (if local) showing the traced program.
Or you can navigate to the wdb.server.py port to see all ongoing debugging sessions on top of the list of running Python programs, which you can then use to access the specific debugging session you want.
Notice that the commands for navigating the code during the trace are different from the standard pdb ones, for example, to step into a function you use .s instead of s and to step over use .n instead of n. See the wdb README in the link above for details.

How to open a "root" file without typing your password every time?

I read this very interesting post, which is very close to what I need but not completely, they use it for shell scripting while I need the same solution mainly for a built-in Python function.
Long story short:
open("/dev/input/event3", "rb")
This doesn't work out of the box because in order to open event3 I need to type my password every single time I execute my Python script, due to elevated privileges. What can I do so I don't have to type my password every time nor write my password in plane text in my script? I like the solution offered in the post I linked above, but this doesn't work because that would mean the -sort of speak- handle I get on this open file will be in another scope/python script.
Any solutions?
Thanks
EDIT
I tried modifying the privileges of my entire Python script, so that I don't need to type a password, but that didn't work neither. What I tried:
1) modify access rights
sudo chown root:root /home/username/myscript.py
sudo chmod 700 /home/username/myscript.py
2) modify visudo
myusername ALL=(ALL) NOPASSWD: /home/username/myscript.py
3) trying to execute my python script now fails, altough it is clearly there
myusername$ ./myscript
bash: ./myscript: No such file or directory
It occurs to me that you may be approaching this problem backward. Currently, you're asking how you can elevate the permissions of a python script without entering a password every time when you should be asking, "why do I need to enter a password at all?"
As long as the file isn't a security concern both the script and the file in question should be owned by non-root users in the input group. The user who owns the python script can then execute it without root privileges to access the file which doesn't require them.
Read more about setuid. It is tricky (so I won't even try to explain it here), and is the basis of authentication related programs like sudo, su, login etc. See also setreuid(2), setuid(2), execve(2), credentials(7), chmod(1).
A good Unix programming book (such as ALP, or something newer) should explain setuid in terms of system calls (listed in syscalls(2)).
setuid executables cannot be scripts (with shebang); they should be binary ELF executables (see elf(5)). However, you could write some setuid wrapper program in C (or most other compiled languages, e.g. Rust, Ocaml, C++, Go, ...) which runs your Python script. Be careful, since a mistake could open a huge security hole. But with such a setuid executable, you won't have to type any password.
You could also have some specific user or group owning the /dev/input/event3 (so configure appropriately your system for that, thru udev or systemd...) and have a setuid or setgid program.
BTW, you could configure sudo (see sudoers(5) and this) to avoid typing any password. Of course, that weakens the security of your entire system (but the choice is yours).
You can pass the sudo password in the same line with the command, using this syntax:
echo password | sudo -S your_command
This way you won't be prompted for sudo password before the command can execute - sounds like that's what you are looking for.

How to make it extremely easy to run a python script for a user

I built a python/bash application for OSX that I want to distribute to non techsavvy users. My problem is how to get the user to initially run my python script. The script sets up a cron job so the user never has to do anything again but the user MUST run it the first time. The issue is that python and bash scripts default to non-executable when unzipped. As of now I have instructions in the readme.txt for the user to open the terminal and copy and paste the command which chmods the script and runs it for them. However is there anyway I can accomplish this without having the user open the Terminal? Ideally the user could just double click the python script and have it run. Thanks.
Sometimes being user-friendly warrants using platform-specific features. In this case, the Mac OS X "Installer" system: http://en.wikipedia.org/wiki/Installer_(OS_X)
Rather than a .zip file, you can create a .pkg file which users can download and install just as they would any other program native to OS X. This should give them a familiar experience, and you can make the .pkg file do what you need behind the scenes.

Enthought Canopy: Where do os.environ variables come from?

I have the following problem. I wanted to use the matplotlib package animation to save an mp4 video file. The save function has a dependency for generating the mp4 file, the ffmpeg external library. So I installed ffmpeg on a Mac osx 10.8 via Macports, and it got installed in /opt/local/bin .
But now, running the script in canopy, the interpreter (ipython shell) can not see ffmpeg. I added the path to my .bash_profile, and I can run the program at my terminal, but when I type os.environ['PATH'] the actual PATH of my shell was not added, and /opt/local/bin is not there.
If I try to run the script, I get this error:
/Users/alejandrodelacallenegro/Library/Enthought/Canopy_64bit/User/lib/python2.7/site- packages/matplotlib/animation.py:695: UserWarning: MovieWriter ffmpeg unavailable
warnings.warn("MovieWriter %s unavailable" % writer)
Any ideas to fix the problem? What I have to do to change an environmental variable that python sees at startup? Did anyone have the same problem?
Thanks.
The problem here has nothing to do with Enthought; it's that OS X doesn't run bash when you launch things from Finder, LaunchDaemons, etc., and therefore doesn't access your .bash_profile. Instead, it runs them from launchd.
If you want to add some environment variables to affect anything run by launchd for the current user, that's easy:
launchctl setenv PATH $PATH:/opt/local/bin
If you want this to happen every time you log in, if you create a file ~/.launchd.conf, the subcommands in that file will be run through launchctl every time launchd starts (which is the first step in logging in a new user session).
If you want it to be system-wide, rather than just for your user, you can sudo launchctl and/or create/edit /etc/launchd.conf. However, you almost certainly don't want to change the environment used by root services, etc., unless you really know what you're doing.
If it helps: Using launchctl manually, editing ~/.launchd.conf, and editing /etc/launchd.conf are roughly equivalent to export, ~/.bash_profile, and /etc/profile (except of course that they affect launchd rather than bash/sh).
See the launchctl(1) man page for details, or just type launchctl to start an interactive session and use the built-in help. (The pages launchd(8) and launchd.conf(5) also have useful info.)
You can also use the deprecated environment.plist file to affect even things that aren't run by launchd, but… that's deprecated, and there really isn't anything for it to affect that you care about, except in (much) older versions of OS X.
People coming from other Unix systems are often caught out by this. Most file managers ask the shell to run programs for them; Finder.app (and the command-line tool open, and the AppleScript environment, and so on) ask launchd to do it. Plus, on most X11 systems, if you look up the process tree from your file manager, it was ultimately launched by a user shell too, whereas on OS X, Finder.app was launched by launchd, which was launched by the system-wide launchd; no shell in sight.
This also means that other shell-specific stuff like changing resource limits or default umask won't affect programs started outside the shell on a Mac. launchctl is again the answer.

Execute Python Script as Root (seteuid vs c-wrapper)

I have a quick one off task in a python script that I'd like to call from Django (www user), that's going to need to root privileges.
At first I thought I would could use Python's os.seteuid() and set the setuid bit on the script, but then I realized that I would have to set the setuid bit on Python itself, which I assume is big no no. From what I can tell, this would also be the case if using sudo, which I really would like to avoid.
At this point, I'm considering just writing a C wrapper the uses seteuid and calls my python script as root, passing the necessary arguments to it.
Is this the correct thing to do or should I be looking at something else?
sudo does not require setuid bit on Python. You can enable sudo for one command only, no arguments:
www ALL=(ALL) NOPASSWD: /root/bin/reload-stuff.py ""
This would be secure if your script does not take any arguments, cannot be overridden by www user, and sudo does "env_reset" (the default in most distros).
You can accept arguments, but be very careful with them -- do not take output filenames, make sure you verify all inputs. In this case, remove "" from the end of sudo line.
The correct thing is called privilege separation: clearly identify minimal set of tasks which have to be done on elevated privileges. Write a separate daemon and an as much limited as possible way of communicating the task to do. Run this daemon as another user with elevated privileges. A bit more work, but also more secure.
EDIT: using a setuid-able wrapper will also satisfy the concept of privilege separation, although I recommend having the web server chrooted and mounting the chrooted file system nosuid (which would defeat that).
sudo allows you to limit arguments passed to the program. From man sudoers:
john ALPHA = /usr/bin/su [!-]*, !/usr/bin/su *root*
On the ALPHA machines, user john may su to anyone except root but
he is not allowed to specify any options to the su(1) command.
So use sudo. Of course you need to be extra careful with root access – make sure only root can modify the script itself and any parent directories, and that the script is safe and only does the absolute minimum that needs to be run as root.

Categories