setresgid/setresuid/setreuid系统调用及示例

setresgid/setresuid/setreuid 函数详解

1. 函数介绍

这三个函数是Linux系统调用,用于设置进程的用户ID和组ID。它们提供了更灵活的权限控制机制,允许同时设置真实ID、有效ID和保存的ID。

  • setresuid: 设置真实用户ID、有效用户ID和保存的用户ID
  • setresgid: 设置真实组ID、有效组ID和保存的组ID
  • setreuid: 设置真实用户ID和有效用户ID(简化版本)

2. 函数原型

#include <unistd.h>

// 设置用户ID
int setresuid(uid_t ruid, uid_t euid, uid_t suid);
int setreuid(uid_t ruid, uid_t euid);

// 设置组ID
int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
int setregid(gid_t rgid, gid_t egid);

3. 功能

这些函数允许进程精确控制其用户和组权限,支持复杂的权限切换场景,是构建安全应用程序的重要工具。

4. 参数

  • ruid/rgid: 真实用户ID/组ID(-1表示不改变)
  • euid/egid: 有效用户ID/组ID(-1表示不改变)
  • suid/sgid: 保存的用户ID/组ID(-1表示不改变,仅setresuid/setresgid)

5. 返回值

  • 成功: 返回0
  • 失败: 返回-1,并设置errno

6. 相似函数,或关联函数

  • setuid/setgid: 设置有效ID
  • seteuid/setegid: 设置有效ID
  • getuid/getgid: 获取真实ID
  • geteuid/getegid: 获取有效ID

7. 示例代码

示例1:基础setresuid/setresgid使用

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>

/**
 * 显示用户和组ID信息
 */
void show_user_group_ids() {
    uid_t ruid = getuid();
    uid_t euid = geteuid();
    gid_t rgid = getgid();
    gid_t egid = getegid();
    
    printf("=== 用户和组ID信息 ===\n");
    printf("真实用户ID: %d", ruid);
    struct passwd *pwd = getpwuid(ruid);
    if (pwd) printf(" (%s)", pwd->pw_name);
    printf("\n");
    
    printf("有效用户ID: %d", euid);
    pwd = getpwuid(euid);
    if (pwd) printf(" (%s)", pwd->pw_name);
    printf("\n");
    
    printf("真实组ID: %d", rgid);
    struct group *grp = getgrgid(rgid);
    if (grp) printf(" (%s)", grp->gr_name);
    printf("\n");
    
    printf("有效组ID: %d", egid);
    grp = getgrgid(egid);
    if (grp) printf(" (%s)", grp->gr_name);
    printf("\n\n");
}

/**
 * 演示基础setresuid/setresgid使用
 */
int demo_basic_usage() {
    uid_t orig_ruid, orig_euid;
    gid_t orig_rgid, orig_egid;
    uid_t test_uid = 1000;
    gid_t test_gid = 1000;
    
    printf("=== 基础setresuid/setresgid使用示例 ===\n");
    
    // 保存原始ID
    orig_ruid = getuid();
    orig_euid = geteuid();
    orig_rgid = getgid();
    orig_egid = getegid();
    
    printf("1. 原始ID信息:\n");
    show_user_group_ids();
    
    // 演示setresuid
    printf("2. 演示setresuid:\n");
    printf("   尝试设置真实用户ID=%d, 有效用户ID=%d, 保存用户ID=%d\n", 
           test_uid, test_uid, test_uid);
    
    int result = setresuid(test_uid, test_uid, test_uid);
    if (result == 0) {
        printf("   ✓ setresuid成功\n");
        printf("   新用户ID信息:\n");
        show_user_group_ids();
    } else {
        printf("   ✗ setresuid失败: %s\n", strerror(errno));
        if (errno == EPERM) {
            printf("   原因:需要root权限\n");
        }
    }
    
    // 演示setresgid
    printf("3. 演示setresgid:\n");
    printf("   尝试设置真实组ID=%d, 有效组ID=%d, 保存组ID=%d\n", 
           test_gid, test_gid, test_gid);
    
    result = setresgid(test_gid, test_gid, test_gid);
    if (result == 0) {
        printf("   ✓ setresgid成功\n");
        printf("   新组ID信息:\n");
        show_user_group_ids();
    } else {
        printf("   ✗ setresgid失败: %s\n", strerror(errno));
        if (errno == EPERM) {
            printf("   原因:需要root权限\n");
        }
    }
    
    // 恢复原始ID
    printf("4. 恢复原始ID:\n");
    result = setresuid(orig_ruid, orig_euid, orig_ruid);
    if (result == 0) {
        printf("   ✓ 恢复用户ID成功\n");
    } else {
        printf("   ✗ 恢复用户ID失败: %s\n", strerror(errno));
    }
    
    result = setresgid(orig_rgid, orig_egid, orig_rgid);
    if (result == 0) {
        printf("   ✓ 恢复组ID成功\n");
    } else {
        printf("   ✗ 恢复组ID失败: %s\n", strerror(errno));
    }
    
    printf("   最终ID信息:\n");
    show_user_group_ids();
    
    return 0;
}

int main() {
    return demo_basic_usage();
}

示例2:权限切换和恢复

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
#include <fcntl.h>

/**
 * 安全权限切换类
 */
typedef struct {
    uid_t saved_ruid;
    uid_t saved_euid;
    uid_t saved_suid;
    gid_t saved_rgid;
    gid_t saved_egid;
    gid_t saved_sgid;
    int valid;
} security_context_t;

/**
 * 保存当前安全上下文
 */
int save_security_context(security_context_t *ctx) {
    ctx->saved_ruid = getuid();
    ctx->saved_euid = geteuid();
    ctx->saved_rgid = getgid();
    ctx->saved_egid = getegid();
    
    // 保存的ID通常需要通过其他方式获取
    ctx->saved_suid = ctx->saved_euid;
    ctx->saved_sgid = ctx->saved_egid;
    ctx->valid = 1;
    
    printf("保存安全上下文:\n");
    printf("  真实UID: %d, 有效UID: %d, 保存UID: %d\n", 
           ctx->saved_ruid, ctx->saved_euid, ctx->saved_suid);
    printf("  真实GID: %d, 有效GID: %d, 保存GID: %d\n", 
           ctx->saved_rgid, ctx->saved_egid, ctx->saved_sgid);
    
    return 0;
}

/**
 * 恢复安全上下文
 */
int restore_security_context(security_context_t *ctx) {
    if (!ctx->valid) {
        printf("无效的安全上下文\n");
        return -1;
    }
    
    printf("恢复安全上下文:\n");
    printf("  目标UID: %d/%d/%d\n", ctx->saved_ruid, ctx->saved_euid, ctx->saved_suid);
    printf("  目标GID: %d/%d/%d\n", ctx->saved_rgid, ctx->saved_egid, ctx->saved_sgid);
    
    int result = setresuid(ctx->saved_ruid, ctx->saved_euid, ctx->saved_suid);
    if (result != 0) {
        printf("恢复UID失败: %s\n", strerror(errno));
        return -1;
    }
    
    result = setresgid(ctx->saved_rgid, ctx->saved_egid, ctx->saved_sgid);
    if (result != 0) {
        printf("恢复GID失败: %s\n", strerror(errno));
        return -1;
    }
    
    printf("安全上下文恢复成功\n");
    return 0;
}

/**
 * 演示权限切换和恢复
 */
int demo_privilege_switching() {
    security_context_t ctx = {0};
    uid_t current_uid = getuid();
    
    printf("=== 权限切换和恢复演示 ===\n");
    
    // 保存当前安全上下文
    if (save_security_context(&ctx) != 0) {
        printf("保存安全上下文失败\n");
        return -1;
    }
    
    // 显示当前状态
    printf("\n当前用户权限状态:\n");
    show_user_group_ids();
    
    // 检查权限
    if (current_uid == 0) {
        printf("✓ 当前具有root权限,可以进行权限切换测试\n");
    } else {
        printf("✗ 当前没有root权限,权限切换可能受限\n");
    }
    
    // 演示临时权限提升
    printf("\n=== 临时权限提升演示 ===\n");
    if (current_uid == 0) {
        // 如果已经是root,演示降权
        uid_t target_uid = 1000;  // 普通用户ID
        gid_t target_gid = 1000;  // 普通组ID
        
        printf("尝试降权到用户 %d, 组 %d\n", target_uid, target_gid);
        
        int result = setresuid(target_uid, target_uid, target_uid);
        if (result == 0) {
            printf("✓ 用户权限降级成功\n");
            result = setresgid(target_gid, target_gid, target_gid);
            if (result == 0) {
                printf("✓ 组权限降级成功\n");
                show_user_group_ids();
                
                // 模拟降权后的工作
                printf("执行降权后的操作...\n");
                sleep(2);
                
                // 恢复权限
                printf("恢复原始权限:\n");
                if (restore_security_context(&ctx) == 0) {
                    show_user_group_ids();
                    printf("✓ 权限成功恢复\n");
                } else {
                    printf("✗ 权限恢复失败\n");
                }
            } else {
                printf("✗ 组权限降级失败: %s\n", strerror(errno));
            }
        } else {
            printf("✗ 用户权限降级失败: %s\n", strerror(errno));
        }
    } else {
        printf("非root用户权限切换演示:\n");
        
        // 演示部分权限切换(仅改变有效ID)
        printf("尝试仅改变有效用户ID:\n");
        int result = setreuid(-1, current_uid);  // -1表示不改变真实ID
        if (result == 0) {
            printf("✓ 有效用户ID设置成功\n");
        } else {
            printf("✗ 有效用户ID设置失败: %s\n", strerror(errno));
        }
        
        show_user_group_ids();
    }
    
    // 验证最终状态
    printf("\n=== 最终状态验证 ===\n");
    uid_t final_ruid = getuid();
    uid_t final_euid = geteuid();
    gid_t final_rgid = getgid();
    gid_t final_egid = getegid();
    
    printf("最终ID状态:\n");
    printf("  真实UID: %d (原始: %d)\n", final_ruid, ctx.saved_ruid);
    printf("  有效UID: %d (原始: %d)\n", final_euid, ctx.saved_euid);
    printf("  真实GID: %d (原始: %d)\n", final_rgid, ctx.saved_rgid);
    printf("  有效GID: %d (原始: %d)\n", final_egid, ctx.saved_egid);
    
    if (final_ruid == ctx.saved_ruid && final_euid == ctx.saved_euid &&
        final_rgid == ctx.saved_rgid && final_egid == ctx.saved_egid) {
        printf("✓ ID状态验证通过\n");
    } else {
        printf("✗ ID状态验证失败\n");
    }
    
    return 0;
}

// 辅助函数声明
void show_user_group_ids();

int main() {
    return demo_privilege_switching();
}

示例3:setreuid/setregid使用

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <sys/wait.h>

/**
 * 演示setreuid/setregid使用
 */
int demo_simplified_functions() {
    uid_t orig_ruid, orig_euid;
    gid_t orig_rgid, orig_egid;
    uid_t test_uid = 1000;
    gid_t test_gid = 1000;
    
    printf("=== setreuid/setregid 使用演示 ===\n");
    
    // 保存原始ID
    orig_ruid = getuid();
    orig_euid = geteuid();
    orig_rgid = getgid();
    orig_egid = getegid();
    
    printf("原始ID信息:\n");
    show_user_group_ids();
    
    // 演示setreuid(仅设置真实和有效用户ID)
    printf("\n1. 演示setreuid:\n");
    printf("   尝试设置真实用户ID=%d, 有效用户ID=%d\n", test_uid, test_uid);
    
    int result = setreuid(test_uid, test_uid);
    if (result == 0) {
        printf("   ✓ setreuid成功\n");
        printf("   新用户ID信息:\n");
        show_user_group_ids();
    } else {
        printf("   ✗ setreuid失败: %s\n", strerror(errno));
        if (errno == EPERM) {
            printf("   原因:需要适当权限\n");
        }
    }
    
    // 演示setregid(仅设置真实和有效组ID)
    printf("\n2. 演示setregid:\n");
    printf("   尝试设置真实组ID=%d, 有效组ID=%d\n", test_gid, test_gid);
    
    result = setregid(test_gid, test_gid);
    if (result == 0) {
        printf("   ✓ setregid成功\n");
        printf("   新组ID信息:\n");
        show_user_group_ids();
    } else {
        printf("   ✗ setregid失败: %s\n", strerror(errno));
        if (errno == EPERM) {
            printf("   原因:需要适当权限\n");
        }
    }
    
    // 演示部分设置(使用-1)
    printf("\n3. 演示部分设置:\n");
    printf("   保持真实用户ID不变,仅设置有效用户ID\n");
    
    result = setreuid(-1, orig_euid);
    if (result == 0) {
        printf("   ✓ 部分设置成功\n");
        show_user_group_ids();
    } else {
        printf("   ✗ 部分设置失败: %s\n", strerror(errno));
    }
    
    // 恢复原始ID
    printf("\n4. 恢复原始ID:\n");
    result = setreuid(orig_ruid, orig_euid);
    if (result == 0) {
        printf("   ✓ 恢复用户ID成功\n");
    } else {
        printf("   ✗ 恢复用户ID失败: %s\n", strerror(errno));
    }
    
    result = setregid(orig_rgid, orig_egid);
    if (result == 0) {
        printf("   ✓ 恢复组ID成功\n");
    } else {
        printf("   ✗ 恢复组ID失败: %s\n", strerror(errno));
    }
    
    printf("   最终ID信息:\n");
    show_user_group_ids();
    
    return 0;
}

/**
 * 子进程演示
 */
void child_process_demo(int child_id) {
    printf("子进程 %d 启动 (PID: %d)\n", child_id, getpid());
    
    printf("子进程 %d 初始ID信息:\n", child_id);
    show_user_group_ids();
    
    // 子进程尝试修改ID
    uid_t new_uid = 1000;
    gid_t new_gid = 1000;
    
    printf("子进程 %d 尝试修改ID:\n", child_id);
    
    int result = setreuid(new_uid, new_uid);
    if (result == 0) {
        printf("  子进程 %d setreuid成功\n", child_id);
    } else {
        printf("  子进程 %d setreuid失败: %s\n", child_id, strerror(errno));
    }
    
    result = setregid(new_gid, new_gid);
    if (result == 0) {
        printf("  子进程 %d setregid成功\n", child_id);
    } else {
        printf("  子进程 %d setregid失败: %s\n", child_id, strerror(errno));
    }
    
    printf("子进程 %d 最终ID信息:\n", child_id);
    show_user_group_ids();
    
    sleep(2);
    printf("子进程 %d 结束\n", child_id);
    exit(0);
}

/**
 * 多进程ID管理演示
 */
int demo_multiprocess_id_management() {
    pid_t child1_pid, child2_pid;
    
    printf("=== 多进程ID管理演示 ===\n");
    
    printf("父进程初始ID信息:\n");
    show_user_group_ids();
    
    // 创建子进程1
    child1_pid = fork();
    if (child1_pid == 0) {
        child_process_demo(1);
    } else if (child1_pid > 0) {
        printf("创建子进程1: PID=%d\n", child1_pid);
    } else {
        perror("创建子进程1失败");
        return -1;
    }
    
    // 创建子进程2
    child2_pid = fork();
    if (child2_pid == 0) {
        child_process_demo(2);
    } else if (child2_pid > 0) {
        printf("创建子进程2: PID=%d\n", child2_pid);
    } else {
        perror("创建子进程2失败");
        kill(child1_pid, SIGKILL);
        return -1;
    }
    
    // 父进程也尝试修改ID
    printf("\n父进程尝试修改ID:\n");
    uid_t parent_new_uid = 2000;
    gid_t parent_new_gid = 2000;
    
    int result = setreuid(parent_new_uid, parent_new_uid);
    if (result == 0) {
        printf("父进程setreuid成功\n");
    } else {
        printf("父进程setreuid失败: %s\n", strerror(errno));
    }
    
    result = setregid(parent_new_gid, parent_new_gid);
    if (result == 0) {
        printf("父进程setregid成功\n");
    } else {
        printf("父进程setregid失败: %s\n", strerror(errno));
    }
    
    printf("父进程修改后ID信息:\n");
    show_user_group_ids();
    
    // 等待子进程结束
    printf("等待子进程结束:\n");
    int status;
    pid_t finished_pid;
    
    while ((finished_pid = wait(&status)) > 0) {
        printf("子进程 %d 已结束,退出状态: %d\n", finished_pid, WEXITSTATUS(status));
    }
    
    return 0;
}

// 辅助函数声明
void show_user_group_ids();

int main() {
    printf("=== setreuid/setregid vs setresuid/setresgid 比较 ===\n");
    printf("setreuid/setregid: 简化版本,只设置真实和有效ID\n");
    printf("setresuid/setresgid: 完整版本,设置真实、有效和保存的ID\n\n");
    
    // 先运行简化函数演示
    demo_simplified_functions();
    
    printf("\n" "=" * 50 "\n");
    
    // 再运行多进程演示
    demo_multiprocess_id_management();
    
    return 0;
}

示例4:安全权限管理

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>

/**
 * 权限管理器
 */
typedef struct {
    uid_t original_ruid;
    uid_t original_euid;
    uid_t original_suid;
    gid_t original_rgid;
    gid_t original_egid;
    gid_t original_sgid;
    int initialized;
} privilege_manager_t;

/**
 * 初始化权限管理器
 */
int init_privilege_manager(privilege_manager_t *pm) {
    pm->original_ruid = getuid();
    pm->original_euid = geteuid();
    pm->original_rgid = getgid();
    pm->original_egid = getegid();
    
    // 保存的ID通常等于有效ID(在程序启动时)
    pm->original_suid = pm->original_euid;
    pm->original_sgid = pm->original_egid;
    pm->initialized = 1;
    
    printf("权限管理器初始化完成\n");
    printf("原始UID: %d/%d/%d\n", pm->original_ruid, pm->original_euid, pm->original_suid);
    printf("原始GID: %d/%d/%d\n", pm->original_rgid, pm->original_egid, pm->original_sgid);
    
    return 0;
}

/**
 * 临时提升到root权限
 */
int escalate_to_root(privilege_manager_t *pm) {
    if (!pm->initialized) {
        printf("权限管理器未初始化\n");
        return -1;
    }
    
    printf("尝试提升到root权限...\n");
    
    // 使用setresuid设置所有ID为0(root)
    int result = setresuid(0, 0, 0);
    if (result == 0) {
        printf("✓ 成功提升到root权限\n");
        show_user_group_ids();
        
        result = setresgid(0, 0, 0);
        if (result == 0) {
            printf("✓ 成功提升到root组权限\n");
        } else {
            printf("✗ 提升组权限失败: %s\n", strerror(errno));
            // 尝试恢复用户权限
            setresuid(pm->original_ruid, pm->original_euid, pm->original_suid);
        }
    } else {
        printf("✗ 提升用户权限失败: %s\n", strerror(errno));
        if (errno == EPERM) {
            printf("  当前没有足够权限进行提升\n");
        }
    }
    
    return result;
}

/**
 * 降权到指定用户
 */
int drop_to_user(privilege_manager_t *pm, uid_t target_uid, gid_t target_gid) {
    if (!pm->initialized) {
        printf("权限管理器未初始化\n");
        return -1;
    }
    
    printf("尝试降权到用户 %d, 组 %d...\n", target_uid, target_gid);
    
    int result = setresuid(target_uid, target_uid, pm->original_suid);
    if (result == 0) {
        printf("✓ 用户权限降级成功\n");
        
        result = setresgid(target_gid, target_gid, pm->original_sgid);
        if (result == 0) {
            printf("✓ 组权限降级成功\n");
            show_user_group_ids();
        } else {
            printf("✗ 组权限降级失败: %s\n", strerror(errno));
        }
    } else {
        printf("✗ 用户权限降级失败: %s\n", strerror(errno));
    }
    
    return result;
}

/**
 * 恢复原始权限
 */
int restore_original_privileges(privilege_manager_t *pm) {
    if (!pm->initialized) {
        printf("权限管理器未初始化\n");
        return -1;
    }
    
    printf("恢复原始权限...\n");
    
    int result = setresuid(pm->original_ruid, pm->original_euid, pm->original_suid);
    if (result == 0) {
        printf("✓ 用户权限恢复成功\n");
        
        result = setresgid(pm->original_rgid, pm->original_egid, pm->original_sgid);
        if (result == 0) {
            printf("✓ 组权限恢复成功\n");
            show_user_group_ids();
        } else {
            printf("✗ 组权限恢复失败: %s\n", strerror(errno));
        }
    } else {
        printf("✗ 用户权限恢复失败: %s\n", strerror(errno));
    }
    
    return result;
}

/**
 * 演示安全权限管理
 */
int demo_secure_privilege_management() {
    privilege_manager_t pm = {0};
    uid_t current_uid = getuid();
    
    printf("=== 安全权限管理演示 ===\n");
    
    // 初始化权限管理器
    if (init_privilege_manager(&pm) != 0) {
        printf("初始化权限管理器失败\n");
        return -1;
    }
    
    printf("\n当前权限状态:\n");
    show_user_group_ids();
    
    // 根据当前权限级别演示不同场景
    if (current_uid == 0) {
        printf("✓ 当前运行在root权限下\n");
        
        // 演示降权操作
        printf("\n=== 降权操作演示 ===\n");
        uid_t target_uid = 1000;  // 普通用户
        gid_t target_gid = 1000;  // 普通组
        
        if (drop_to_user(&pm, target_uid, target_gid) == 0) {
            printf("降权操作成功\n");
            
            // 模拟降权后的操作
            printf("执行降权后的安全操作...\n");
            sleep(2);
            
            // 恢复原始权限
            printf("\n=== 权限恢复演示 ===\n");
            if (restore_original_privileges(&pm) == 0) {
                printf("权限成功恢复到原始状态\n");
            } else {
                printf("权限恢复失败\n");
            }
        }
    } else {
        printf("✗ 当前运行在普通用户权限下\n");
        
        // 演示权限检查和安全操作
        printf("\n=== 权限检查演示 ===\n");
        
        // 尝试提升权限(应该失败)
        printf("尝试提升权限:\n");
        int result = setresuid(0, 0, 0);
        if (result == 0) {
            printf("✓ 权限提升成功(意外情况)\n");
        } else {
            printf("✗ 权限提升失败: %s\n", strerror(errno));
            printf("  这是预期的行为\n");
        }
        
        // 演示部分权限切换
        printf("\n演示部分权限切换:\n");
        result = setreuid(-1, current_uid);  // 仅改变有效ID
        if (result == 0) {
            printf("✓ 部分权限切换成功\n");
        } else {
            printf("✗ 部分权限切换失败: %s\n", strerror(errno));
        }
    }
    
    // 安全审计
    printf("\n=== 安全审计 ===\n");
    uid_t final_ruid = getuid();
    uid_t final_euid = geteuid();
    gid_t final_rgid = getgid();
    gid_t final_egid = getegid();
    
    printf("最终权限状态:\n");
    printf("  真实UID: %d (原始: %d)\n", final_ruid, pm.original_ruid);
    printf("  有效UID: %d (原始: %d)\n", final_euid, pm.original_euid);
    printf("  真实GID: %d (原始: %d)\n", final_rgid, pm.original_rgid);
    printf("  有效GID: %d (原始: %d)\n", final_egid, pm.original_egid);
    
    if (final_ruid == pm.original_ruid && final_euid == pm.original_euid &&
        final_rgid == pm.original_rgid && final_egid == pm.original_egid) {
        printf("✓ 权限状态审计通过\n");
    } else {
        printf("⚠ 权限状态发生变化,需要关注\n");
    }
    
    return 0;
}

// 辅助函数声明
void show_user_group_ids();

int main() {
    return demo_secure_privilege_management();
}

示例5:权限切换最佳实践

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>

/**
 * 权限切换安全检查
 */
typedef enum {
    PRIV_CHECK_SUCCESS = 0,
    PRIV_CHECK_PERMISSION_DENIED,
    PRIV_CHECK_INVALID_ID,
    PRIV_CHECK_OTHER_ERROR
} privilege_check_result_t;

/**
 * 检查权限切换的可行性
 */
privilege_check_result_t check_privilege_switch_feasibility(uid_t target_uid, gid_t target_gid) {
    uid_t current_ruid = getuid();
    uid_t current_euid = geteuid();
    gid_t current_rgid = getgid();
    gid_t current_egid = getegid();
    
    printf("权限切换可行性检查:\n");
    printf("  当前真实UID: %d, 有效UID: %d\n", current_ruid, current_euid);
    printf("  当前真实GID: %d, 有效GID: %d\n", current_rgid, current_egid);
    printf("  目标UID: %d, 目标GID: %d\n", target_uid, target_gid);
    
    // 检查是否已经是目标权限
    if (current_ruid == target_uid && current_euid == target_uid &&
        current_rgid == target_gid && current_egid == target_gid) {
        printf("  ✓ 已经是目标权限\n");
        return PRIV_CHECK_SUCCESS;
    }
    
    // 检查是否具有足够权限
    if (current_ruid == 0 || current_euid == 0) {
        printf("  ✓ 具有root权限,可以切换到任意权限\n");
        return PRIV_CHECK_SUCCESS;
    }
    
    // 检查普通用户的权限切换限制
    if ((target_uid == current_ruid || target_uid == current_euid) &&
        (target_gid == current_rgid || target_gid == current_egid)) {
        printf("  ✓ 可以切换到目标权限(在允许范围内)\n");
        return PRIV_CHECK_SUCCESS;
    }
    
    printf("  ✗ 权限不足,无法切换到目标权限\n");
    return PRIV_CHECK_PERMISSION_DENIED;
}

/**
 * 安全的权限切换函数
 */
int safe_privilege_switch(uid_t target_uid, gid_t target_gid, int preserve_saved_ids) {
    uid_t current_ruid = getuid();
    uid_t current_euid = geteuid();
    gid_t current_rgid = getgid();
    gid_t current_egid = getegid();
    
    printf("执行安全权限切换:\n");
    printf("  从 UID %d/%d, GID %d/%d\n", 
           current_ruid, current_euid, current_rgid, current_egid);
    printf("  切换到 UID %d, GID %d\n", target_uid, target_gid);
    
    // 使用setresuid/setresgid进行完整切换
    uid_t new_ruid = target_uid;
    uid_t new_euid = target_uid;
    uid_t new_suid = preserve_saved_ids ? -1 : target_uid;
    
    gid_t new_rgid = target_gid;
    gid_t new_egid = target_gid;
    gid_t new_sgid = preserve_saved_ids ? -1 : target_gid;
    
    int result;
    
    // 先切换组ID(通常更安全)
    result = setresgid(new_rgid, new_egid, new_sgid);
    if (result != 0) {
        printf("  ✗ 组权限切换失败: %s\n", strerror(errno));
        return -1;
    }
    
    // 再切换用户ID
    result = setresuid(new_ruid, new_euid, new_suid);
    if (result != 0) {
        printf("  ✗ 用户权限切换失败: %s\n", strerror(errno));
        // 尝试恢复组ID
        setresgid(current_rgid, current_egid, -1);
        return -1;
    }
    
    printf("  ✓ 权限切换成功\n");
    show_user_group_ids();
    
    return 0;
}

/**
 * 权限切换回滚机制
 */
typedef struct {
    uid_t saved_ruid;
    uid_t saved_euid;
    gid_t saved_rgid;
    gid_t saved_egid;
    int valid;
} rollback_info_t;

/**
 * 保存回滚信息
 */
int save_rollback_info(rollback_info_t *rb) {
    rb->saved_ruid = getuid();
    rb->saved_euid = geteuid();
    rb->saved_rgid = getgid();
    rb->saved_egid = getegid();
    rb->valid = 1;
    
    printf("保存回滚信息: UID %d/%d, GID %d/%d\n",
           rb->saved_ruid, rb->saved_euid, rb->saved_rgid, rb->saved_egid);
    
    return 0;
}

/**
 * 执行回滚
 */
int perform_rollback(rollback_info_t *rb) {
    if (!rb->valid) {
        printf("无效的回滚信息\n");
        return -1;
    }
    
    printf("执行权限回滚:\n");
    printf("  目标 UID %d/%d, GID %d/%d\n",
           rb->saved_ruid, rb->saved_euid, rb->saved_rgid, rb->saved_egid);
    
    // 恢复权限
    int result = setresuid(rb->saved_ruid, rb->saved_euid, -1);
    if (result != 0) {
        printf("  ✗ 用户权限回滚失败: %s\n", strerror(errno));
        return -1;
    }
    
    result = setresgid(rb->saved_rgid, rb->saved_egid, -1);
    if (result != 0) {
        printf("  ✗ 组权限回滚失败: %s\n", strerror(errno));
        return -1;
    }
    
    printf("  ✓ 权限回滚成功\n");
    show_user_group_ids();
    
    return 0;
}

/**
 * 演示权限切换最佳实践
 */
int demo_best_practices() {
    rollback_info_t rb = {0};
    uid_t current_uid = getuid();
    
    printf("=== 权限切换最佳实践演示 ===\n");
    
    // 1. 保存当前状态用于回滚
    printf("1. 保存当前权限状态\n");
    if (save_rollback_info(&rb) != 0) {
        printf("保存回滚信息失败\n");
        return -1;
    }
    
    // 2. 检查权限切换可行性
    printf("\n2. 检查权限切换可行性\n");
    privilege_check_result_t check_result = check_privilege_switch_feasibility(1000, 1000);
    if (check_result != PRIV_CHECK_SUCCESS) {
        printf("权限切换不可行,跳过演示\n");
        return 0;
    }
    
    // 3. 执行安全权限切换
    printf("\n3. 执行安全权限切换\n");
    if (current_uid == 0) {
        // 如果是root,演示降权
        printf("当前为root权限,演示安全降权:\n");
        if (safe_privilege_switch(1000, 1000, 1) == 0) {
            printf("✓ 安全降权成功\n");
            
            // 模拟降权后的操作
            printf("执行降权后的安全操作...\n");
            sleep(2);
            
            // 4. 执行权限回滚
            printf("\n4. 执行权限回滚\n");
            if (perform_rollback(&rb) == 0) {
                printf("✓ 权限成功回滚到原始状态\n");
            } else {
                printf("✗ 权限回滚失败\n");
            }
        } else {
            printf("✗ 安全降权失败\n");
        }
    } else {
        // 如果是普通用户,演示权限检查
        printf("当前为普通用户权限,演示权限检查:\n");
        
        // 尝试切换到相同权限(应该成功)
        if (safe_privilege_switch(current_uid, getgid(), 1) == 0) {
            printf("✓ 相同权限切换成功\n");
        }
        
        // 尝试切换到不同权限(应该失败)
        printf("尝试切换到不同权限:\n");
        int result = setresuid(0, 0, 0);  // 尝试切换到root
        if (result == 0) {
            printf("✗ 意外获得root权限\n");
        } else {
            printf("✓ 权限切换被正确拒绝: %s\n", strerror(errno));
        }
    }
    
    // 5. 显示最佳实践总结
    printf("\n=== 权限切换最佳实践总结 ===\n");
    printf("1. ✓ 始终保存原始权限状态用于回滚\n");
    printf("2. ✓ 在切换前检查权限可行性\n");
    printf("3. ✓ 使用setresuid/setresgid进行完整控制\n");
    printf("4. ✓ 先切换组权限,再切换用户权限\n");
    printf("5. ✓ 及时回滚到原始权限状态\n");
    printf("6. ✓ 记录权限切换操作日志\n");
    printf("7. ✓ 遵循最小权限原则\n");
    printf("8. ✓ 在特权操作后立即降权\n");
    
    // 6. 安全审计
    printf("\n=== 最终安全审计 ===\n");
    uid_t final_ruid = getuid();
    uid_t final_euid = geteuid();
    gid_t final_rgid = getgid();
    gid_t final_egid = getegid();
    
    printf("最终权限状态:\n");
    printf("  真实UID: %d (原始: %d)\n", final_ruid, rb.saved_ruid);
    printf("  有效UID: %d (原始: %d)\n", final_euid, rb.saved_euid);
    printf("  真实GID: %d (原始: %d)\n", final_rgid, rb.saved_rgid);
    printf("  有效GID: %d (原始: %d)\n", final_egid, rb.saved_egid);
    
    if (final_ruid == rb.saved_ruid && final_euid == rb.saved_euid &&
        final_rgid == rb.saved_rgid && final_egid == rb.saved_egid) {
        printf("✓ 权限状态已正确恢复\n");
    } else {
        printf("⚠ 权限状态异常,需要手动检查\n");
    }
    
    return 0;
}

// 辅助函数声明
void show_user_group_ids();

int main() {
    return demo_best_practices();
}

使用注意事项

系统要求:

  1. 内核版本: 支持完整权限管理的Linux内核
  2. 权限要求: 需要相应权限才能执行权限切换
  3. 架构支持: 支持所有主流架构

权限规则:

  1. 超级用户: 可以设置任意ID
  2. 普通用户: 只能设置为允许的值
  3. 保存的ID: 通常在exec时设置

错误处理:

  1. EPERM: 权限不足
  2. EINVAL: ID值无效
  3. EAGAIN: 资源暂时不可用

安全考虑:

  1. 权限提升: 可能导致安全风险
  2. 审计日志: 建议记录权限变更
  3. 最小权限: 遵循最小权限原则
  4. 状态恢复: 及时恢复原始状态

最佳实践:

  1. 保存状态: 执行前保存原始权限状态
  2. 检查权限: 验证是否有足够权限
  3. 错误处理: 妥善处理各种错误情况
  4. 及时恢复: 使用完特权后及时恢复
  5. 日志记录: 记录权限变更操作

函数对比

函数参数数量功能适用场景
setreuid2个设置真实和有效用户ID简单权限切换
setregid2个设置真实和有效组ID简单权限切换
setresuid3个设置真实、有效和保存用户ID完整权限控制
setresgid3个设置真实、有效和保存组ID完整权限控制

总结

setresuid/setresgid/setreuid/setregid 提供了灵活的权限管理机制:

  1. 精确控制: 可以精确控制进程的权限状态
  2. 安全机制: 通过权限检查保证系统安全
  3. 灵活使用: 支持部分参数设置(使用-1)
  4. 标准兼容: 符合POSIX标准

在实际应用中,需要:

  • 遵循安全最佳实践
  • 妥善处理错误情况
  • 及时恢复原始权限状态
  • 记录权限变更日志
此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注