ZetCode

Python os.geteuid Function

Last modified April 11, 2025

This comprehensive guide explores Python's os.geteuid function, which returns the effective user ID of the current process. We'll cover Unix user IDs, real vs effective UIDs, and practical permission checking examples.

Basic Definitions

The os.geteuid function returns the numeric effective user ID of the current process. This is a Unix-specific function that doesn't work on Windows systems.

Effective UID determines what files and resources a process can access. It may differ from the real UID when running setuid programs or when privileges are changed.

Getting the Effective User ID

The simplest use of os.geteuid retrieves the current process's effective user ID. This example shows basic retrieval and comparison.

basic_geteuid.py
import os

# Get effective user ID
euid = os.geteuid()
print(f"Effective UID: {euid}")

# Compare with real UID
if euid == os.getuid():
    print("Effective UID matches real UID")
else:
    print("Effective UID differs from real UID")

This code retrieves the effective UID and compares it with the real UID. For most normal processes, these values will be the same.

The effective UID is what the system uses for permission checks, while the real UID identifies the user who started the process.

Checking Root Privileges

A common use of os.geteuid is checking if a process is running with root privileges (UID 0). This example demonstrates root privilege check.

check_root.py
import os
import sys

# Check for root privileges
if os.geteuid() == 0:
    print("Running with root privileges")
else:
    print("Not running as root")
    print("This program requires root privileges")
    sys.exit(1)

# Root-only operation example
with open("/etc/shadow", "r") as f:
    print("Successfully accessed /etc/shadow")

This script checks for root privileges before attempting to access a root-protected file. It exits if not running as root.

Note that checking UID 0 is the proper way to detect root privileges, not checking for the username "root".

Comparing Real and Effective UIDs

This example demonstrates the difference between real and effective UIDs by temporarily changing privileges. Requires root to run fully.

compare_uids.py
import os

def print_uids():
    print(f"Real UID: {os.getuid()}")
    print(f"Effective UID: {os.geteuid()}")

print("Initial state:")
print_uids()

# Temporarily drop privileges (requires root)
if os.geteuid() == 0:
    original_euid = os.geteuid()
    os.seteuid(1000)  # Change to a normal user
    print("\nAfter seteuid(1000):")
    print_uids()
    
    # Restore original privileges
    os.seteuid(original_euid)
    print("\nAfter restoring privileges:")
    print_uids()
else:
    print("\nCannot demonstrate privilege changes - run as root")

This script shows how to temporarily drop and restore privileges while maintaining the real UID. The effective UID changes affect permissions.

Note that os.seteuid only changes the effective UID, while os.setuid changes both real and effective UIDs.

Setuid Program Example

This example simulates a setuid program behavior where effective and real UIDs differ. Requires root to set up properly.

setuid_example.py
import os
import pwd

def print_user_info():
    try:
        euser = pwd.getpwuid(os.geteuid()).pw_name
        ruser = pwd.getpwuid(os.getuid()).pw_name
        print(f"Real user: {ruser} (UID: {os.getuid()})")
        print(f"Effective user: {euser} (UID: {os.geteuid()})")
    except KeyError:
        print("Could not resolve UID to username")

print("Starting as:")
print_user_info()

# Simulate setuid behavior (must be run as root)
if os.geteuid() == 0:
    print("\nDropping privileges to nobody:")
    nobody = pwd.getpwnam("nobody")
    os.setegid(nobody.pw_gid)
    os.seteuid(nobody.pw_uid)
    print_user_info()
    
    print("\nRestoring root privileges:")
    os.seteuid(0)
    os.setegid(0)
    print_user_info()
else:
    print("\nRun as root to see privilege change demonstration")

This script demonstrates how setuid programs can temporarily assume different privileges while maintaining the original user identity.

The example uses both UID and GID changes for completeness, though geteuid only deals with user IDs.

Checking File Access Permissions

This example shows how to use os.geteuid with file permissions to implement custom access checks beyond standard Unix permissions.

file_access_check.py
import os
import stat

def check_file_access(path):
    try:
        st = os.stat(path)
    except FileNotFoundError:
        return False
    
    # Check if owner matches our effective UID
    if st.st_uid == os.geteuid():
        mode = "owner"
    # Check if group matches any of our groups
    elif st.st_gid in os.getgroups():
        mode = "group"
    else:
        mode = "other"
    
    # Check read permission based on mode
    if mode == "owner" and st.st_mode & stat.S_IRUSR:
        return True
    elif mode == "group" and st.st_mode & stat.S_IRGRP:
        return True
    elif mode == "other" and st.st_mode & stat.S_IROTH:
        return True
    
    return False

file_path = "/etc/passwd"
if check_file_access(file_path):
    print(f"Read access granted to {file_path}")
else:
    print(f"Read access denied to {file_path}")

This custom access check function mimics Unix permission checks but could be extended with additional rules. It uses geteuid to determine ownership.

The function checks owner, group, and other permissions separately based on the effective UID and group membership.

Security Context Demonstration

This example shows how effective UID affects the security context of child processes and file operations. Requires root to demonstrate fully.

security_context.py
import os
import subprocess

print(f"Parent process - EUID: {os.geteuid()}")

# Create a test file
test_file = "euid_test.txt"
with open(test_file, "w") as f:
    f.write("Test content")

# Show file ownership
st = os.stat(test_file)
print(f"File owner UID: {st.st_uid}")

# Fork a child process
pid = os.fork()
if pid == 0:
    print(f"Child process - EUID: {os.geteuid()}")
    
    # Try to modify the file
    try:
        with open(test_file, "a") as f:
            f.write("\nChild process addition")
        print("Child successfully modified file")
    except PermissionError:
        print("Child failed to modify file")
    
    os._exit(0)

# Parent waits for child
os.waitpid(pid, 0)

# Change EUID and repeat (requires root)
if os.geteuid() == 0:
    original_euid = os.geteuid()
    os.seteuid(1000)  # Change to normal user
    
    pid = os.fork()
    if pid == 0:
        print(f"\nChild with changed EUID: {os.geteuid()}")
        try:
            with open(test_file, "a") as f:
                f.write("\nChanged EUID addition")
            print("Child with changed EUID modified file")
        except PermissionError:
            print("Child with changed EUID failed to modify file")
        
        os._exit(0)
    
    os.waitpid(pid, 0)
    os.seteuid(original_euid)  # Restore EUID

# Clean up
os.unlink(test_file)

This example demonstrates how child processes inherit the effective UID and how changing it affects file operations. The second part requires root.

The example shows that file access permissions depend on the effective UID at the time of the operation, not when the process started.

Security Considerations

Best Practices

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.