What is difference between os.getuid() and os.geteuid()? - python

The documentation for os.getuid() says:
Return the current process’s user id.
And of os.geteuid() says:
Return the current process’s effective user id.
So what is the difference between user id and effective user id?
For me both works same (on both 2.x and 3.x). I am using it to check if script is being run as root.

To understand how os.getuid and os.geteuid differ, you need to understand that they're are not Python specific functions (other than the os module prefix). Those functions are wrapping the getuid and geteuid system calls that are provided by essentially all Unix-like operating systems.
So, rather than looking at Python docs (which are not likely to give a lot of details), you should look at the docs for your operating system. Here is the relevant documentation for Linux, for example. Wikipedia also has a good article on Unix User IDs.
The difference between the regular UID and the Effective UID is that only the EUID is checked when you do something that requires special access (such as reading or writing a file, or making certain system calls). The UID indicates the actual user who is performing the action, but it is (usually) not considered when examining permissions. In normal programs they will be the same. Some programs change their EUID to add or subtract from the actions they are allowed to take. A smaller number also change their UID, to effectively "become" another user.
Here's an example a program that changes its EUID: The passwd program (which is used to change your password) must write to the system's password file, which is owned by the root user. Regular users can't write to that file, since if they could, they could change everyone else's password too. To resolve this, the passwd program has a bit set in its file permissions (known as the setuid bit) that indicates to the OS that it should be run with the EUID of the program's owner (e.g. root) even when it is launched by another user. The passwd program would then see its UID as the launching user, and its EUID as root. Writing to the system password file requires the EUID to be privileged. The UID is useful too, since passwd needs to know which user it's changing the password for.
There are a few other cases where the UID and EUID won't match, but they're not too common. For instance, a file server running as the super user might change its EUID to match a specific user who is requesting some file manipulations. Using the user's EUID allows the server to avoid accessing things that the user is not allowed to touch.

Function os.getuid() returns ID of a user who runs your program. Function os.geteuid() of a user your program use permissions of. In most cases this will be the same. Well known case when these values will be different is when setuid bit is set for your program executable file, and user that runs your program is different from user that own program executable. In this case os.getuid() will return ID of user who runs program, while os.geteuid() will return ID of user who own program executable.

Related

Is it possible to restrict access to globals for a block of code in python?

I would like users of my program to be able to define custom scripts in python without breaking the program. I am looking at something like this:
def call(script):
code = "access modification code" + script
exec(code)
Where "access modification code" defines a scope such that script can only access variables it instantiates itself. Is it possible to do this or something with similar functionality, such as creating a new python environment with its own scope and then receiving output from it?
Thank you for your time :)
Clarification Edit
"I want to prevent both active attacks and accidental interaction with the program variables outside the users script (hence hiding all globals). The user scripts are intended to be small and inputted as text. The return of the user script needs to be immediate, as though it were native to the program."
There's two separate problems you want to prevent in this scenario:
Prevent an outside attacker from running arbitrary code in the context of the OS user executing your program. This means preventing arbitrary code execution and privilege escalation.
Preventing a legitimate user of your program from changing the program behavior in unintended ways.
For the first problem, you need to make sure that the source of the python code you execute is the user. You shouldn't accept input from a socket, or from a file that other users can write to. You need to make sure, somehow, that it was the user who is running the program that provided the input.
Actual solutions will depend on your OS, but you may want to consider setting up restrictive file permissions, if you're storing the code in a file.
Don't ignore or downplay this problem, or your users will fall victim to virus/malware/hackers thanks to your program.
The way you solve the second problem depends on what exactly constitutes intended behaviour in your program. If you're happy with outputting simple data structures, you can run your user-inputted code in a separate process, and pass the result over a pipe in a serialization format such as JSON or YAML.
Here's a very simple example:
#remember to set restrictive file permissions on this file. This is OS-dependent
USER_CODE_FILE="/home/user/user_code_file.py"
#absolute path to python binary (executable)
PYTHON_PATH="/usr/bin/python"
import subprocess
import json
user_code= '''
import json
my_data= {"a":[1,2,3]}
print json.dumps(my_data)
'''
with open(USER_CODE_FILE,"wb") as f:
f.write(user_code)
user_result_str= subprocess.check_output([PYTHON_PATH, USER_CODE_FILE])
user_result= json.loads( user_result_str )
print user_result
This is a fairly simple solution, and it has significant overhead. Don't use this if you need to run the user code many times in a short period of time.
Ultimately, this solution is only effective against unsophisticated attackers (users). Generally speaking, there's no way to protect any user process from the user itself - nor would it make much sense.
If you really want more assurance, and want to mitigate the first problem too, you should run the process as a separate ("guest") user. This, again, is OS-dependent.
Finally a warning: avoid exec and eval to the best of your abilities. They don't protect either your program or the user against the inputted code. There's a lot of information about this on the web, just search for "python secure eval"

Running subprocess commands as root in python

I'm writing a GUI program, that configures your systems settings. For this purpose, the whole program should not be ran as root, otherwise it would configure the system for the root user. However, there is a subprocess command that needs to be ran as root, and I'm not sure how to safely, and properly incorporate this into my GUI for the following reasons.
The user would almost have to enter it into the GUI frontend.
I'm not sure how to verify that the users password was indeed correct. How to add error proofing to alert the user that the password is incorrect, without just letting the command fail miserably.
How to run this safely, since the users password is going to be involved.
I've been reccomended to create a daemon, and pass commands to that. This seems like a bit overkill, since it's just one command that needs to be ran. And since the user can't just type this into the terminal, it needs to be handled by the frontend of the GUI.
Does anyone have any other ideas on how to incorporate this feature?
You can use pkexec.
For example:
proc = subprocess.Popen(['/usr/bin/pkexec', command])

How to properly sanitize a filename (protect against shell injection)?

What is the common practice to sanitize a filename from an outside source (e.g.: xml file) before using it within a subprocess (shell=False)?
Update:
Before sending some parsed strings around I would like to make some basic security checks. The given example uses mpg123 (a command line audioplayer) in remote mode to play a sound file.
filename = child.find("filename").text # e.g.: filename = "sound.mp3"
pid = subprocess.Popen(["mpg123"],"-R"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
command = "L "+filename+"\n"
pid.stdin.write(command.encode())
There's a couple of things I can think of.
A lightweight verification can be done if the systems are tolerant. It may also be appropriate if there is little chance of data destruction, or compromise of sensitive data. You can test to see if the string your given is an actual file by using os.path.isfile.
A more classic "secure" programming design would have you index the acceptable files that can be played and do a lookup based on user input. In this way you never actually pass on user input. It get's "filtered" by the lookup to already validated data (the accepted playable files list).
"Sanitizing" input is a black-listing type of technique. They are always less secure than a white-listing type of technique (above). If you have no choice but to "sanitize" the data you have to understand how that data passes through your system, and any other systems you depend on. You then have to craft rules to take into account any flaws or limitations within ALL of the systems. You would also have to cover classic malicious input cases like data input size, unacceptable character encoding and others.
Filenames doesn't need to be sanitized unless you are using a shell or executing anything. Pythons open() will not execute any commands in the filename given.
For security checks, to avoid overwriting files, you use the permission system of your OS, and make sure that the user under which the program is running only can overwrite and access files it should be able to overwrite and access.
It's generally not a good idea to let any program that takes input from the net or another process to accept absolute path names. In this case it should only be allowed to specify files under a defined music folder. I don't think the mp3 player can cause damage by giving it the wrong file, but you can crash it, at least, and that would be annoying.

Executing shell commands as given UID in Python

I need a way to execute the os.system() module as different UID's. It would need to behave similar to the following BASH code (note these are not the exact commands I am executing):
su user1
ls ~
mv file1
su user2
ls ~
mv file1
The target platform is GNU Linux Generic.
Of course I could just pass these to the os.system module, but how to send the password? Of course I could run the script as root, but that's sloppy and insecure.
Preferably I would like to do with without requiring any passwords to be in plain text.
I think that's not trivial: you can do that with a shell because each command is launched into its own process, which has its own id. But with python, everything will have the uid of the python interpreted process (of course, assuming you don't launch subprocesses using the subprocess module and co). I don't know a way of changing the user of a process - I don't know if that's even possible - even if it were, you would at least need admin privileges.
What are you trying to do exactly ? This does not sound like the right thing to do for admin purpose, for example. Generally, admin scripts run in a priviledge user - because nobody knows the password of user 2 except user 2 (in theory). Being root means su user always work for a 'normal' user, without requesting password.
maybe sudo can help you here, otherwise you must be root to execute os.setuid
alternatively if you want to have fun you can use pexpect to do things
something like this, you can improve over this
p = pexpect.spawn("su guest")
p.logfile = sys.stdout
p.expect('Password:')
p.sendline("guest")
The function you're looking for is called os.seteuid. I'm afraid you probably won't escape executing the script as root, in any case, but I think you can use the capabilities(7) framework to 'fence in' the execution a little, so that it can change users--but not do any of the other things the superuser can.
Alternatively, you might be able to do this with PAM. But generally speaking, there's no 'neat' way to do this, and David Cournapeau is absolutely right that it's traditional for admin scripts to run with privileges.
Somewhere along the line, some process or other is going to need an effective UID of 0 (root), because only such a process can set the effective UID to an arbitrary other UID.
At the shell, the su command is a SUID root program; it is appropriately privileged (POSIX jargon) and can set the real and effective UID. Similarly, the sudo command can do the same job. With sudo, you can also configure which commands and UID are allowed. The crucial difference is that su requires the target user's password to let you in; sudo requires the password of the user running it.
There is, of course, the issue of whether a user should know the passwords of other users. In general, no user should know any other user's password.
Scripting UID changes is hard. You can do:
su altuser -c "commands to execute as altuser"
sudo -u altuser commands to execute as altuser
However, su will demand a password from the controlling terminal (and will fail if there is no controlling terminal). If you use sudo, it will cache credentials (or can be configured to do so) so you only get asked once for a password - but it will prompt the first time just like su does.
Working around the prompting is hard. You can use tools parallel to expect which handle pseudo-ttys for you. However, you are then faced with storing passwords in scripts (not a good idea) or somehow stashing them out of sight.
The tool I use for the job is one I wrote, called asroot. It allows me to control precisely the UID and GID attributes that the child process should have. But it is designed to only allow me to use it - that is, at compile time, the authorized username is specified (of course, that can be changed). However, I can do things like:
asroot -u someone -g theirgrp -C -A othergrp -m 022 -- somecmd arg1 ...
This sets the real and effective UID to 'someone', sets the primary group to 'theirgrp', removes all auxilliary groups, and adds 'othergrp' (so the process belongs to just two groups) and sets the umask to 0222; it then executes 'somecmd' with the arguments given.
For a specific user who needs limited (or not so limited) access to other user accounts, this works well. As a general solution, it is not so hot; sudo is better in most respects, but still requires a password (which asroot does not).

Admin privileges for script

how can i check admin-privileges for my script during running?
The concept of "admin-privileges" in our day of fine grained privilege control is becoming hard to define. If you are running on unix with "traditional" access control model, getting the effective user id (available in os module) and checking that against root (0) could be what you are looking for. If you know accessing a file on the system requires the privileges you want your script to have, you can use the os.access() to check if you are privileged enough.
Unfortunately there is no easy nor portable method to give. You need to find out or define the security model used, what system provided APIs are available to query and set privileges and try to locate (or possibly implement yourself) the appropriate python modules that can be used to access the API.
The classic question, why do you need to find out? What if your script tries to do what it needs to do and "just" catches and properly handles failures?
On Unix you can check whether you are root using the os.getuid function:
os.getuid() == 0 and "root" or "not root"
If you're just trying to see if you have access to a certain file that requires administrative rights, a good way to check would be:
import os
print os.access("/path/to/file", os.W_OK)
#replace W_OK with R_OK to test for read permissions
On the other hand, if you really need to know if a user is an administrative account, you can also use this code on Windows 2000 and higher:
import ctypes
print ctypes.windll.shell32.IsUserAnAdmin()
Therefore, a better, cross-platform way to find out if an user is an administrator is:
import ctypes, os
try:
is_admin = os.getuid() == 0
except:
is_admin = ctypes.windll.shell32.IsUserAnAdmin()
print is_admin
Of course, this method will only detect if the user is root on Unix, or is a member of the Administrators group on Windows. However, this is sufficient for most purposes, in my opinion.
Also note that this will fail on Windows versions below 2000, as well as Windows ME, since those are DOS-based versions of Windows and don't have any notion of permissions.

Categories

Resources