ZetCode

Python os.geteuid 函数

上次修改时间:2025 年 4 月 11 日

本综合指南探讨了 Python 的 os.geteuid 函数,该函数返回当前进程的有效用户 ID。我们将涵盖 Unix 用户 ID、实际 UID 与有效 UID 的区别以及实际的权限检查示例。

基本定义

os.geteuid 函数返回当前进程的数字有效用户 ID。这是一个特定于 Unix 的函数,在 Windows 系统上不起作用。

有效 UID 决定了进程可以访问哪些文件和资源。当运行 setuid 程序或更改权限时,它可能与实际 UID 不同。

获取有效用户 ID

os.geteuid 的最简单用法是检索当前进程的有效用户 ID。此示例显示了基本检索和比较。

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")

此代码检索有效 UID 并将其与实际 UID 进行比较。对于大多数正常进程,这些值将相同。

有效 UID 是系统用于权限检查的值,而实际 UID 标识启动该进程的用户。

检查 Root 权限

os.geteuid 的一个常见用途是检查进程是否以 root 权限(UID 0)运行。此示例演示了 root 权限检查。

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")

此脚本在尝试访问受 root 保护的文件之前检查 root 权限。如果未以 root 身份运行,则退出。

请注意,检查 UID 0 是检测 root 权限的正确方法,而不是检查用户名 "root"。

比较实际 UID 和有效 UID

此示例演示了通过临时更改权限来区分实际 UID 和有效 UID。需要 root 才能完全运行。

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")

此脚本显示如何在保持实际 UID 的同时临时放弃和恢复权限。有效 UID 的更改会影响权限。

请注意,os.seteuid 仅更改有效 UID,而 os.setuid 同时更改实际 UID 和有效 UID。

Setuid 程序示例

此示例模拟了有效 UID 和实际 UID 不同的 setuid 程序行为。需要 root 才能正确设置。

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")

此脚本演示了 setuid 程序如何在保持原始用户身份的同时临时承担不同的权限。

该示例同时使用 UID 和 GID 更改,以确保完整性,尽管 geteuid 仅处理用户 ID。

检查文件访问权限

此示例展示了如何将 os.geteuid 与文件权限结合使用,以实现超出标准 Unix 权限的自定义访问检查。

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}")

此自定义访问检查函数模仿 Unix 权限检查,但可以扩展更多规则。它使用 geteuid 来确定所有权。

该函数根据有效 UID 和组成员身份分别检查所有者、组和其他权限。

安全上下文演示

此示例显示了有效 UID 如何影响子进程和文件操作的安全上下文。 需要 root 才能完全演示。

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)

此示例演示了子进程如何继承有效 UID 以及更改它如何影响文件操作。第二部分需要 root。

该示例表明,文件访问权限取决于操作时的有效 UID,而不是进程启动时。

安全注意事项

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一名充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程