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();
}
使用注意事项
系统要求:
- 内核版本: 支持完整权限管理的Linux内核
- 权限要求: 需要相应权限才能执行权限切换
- 架构支持: 支持所有主流架构
权限规则:
- 超级用户: 可以设置任意ID
- 普通用户: 只能设置为允许的值
- 保存的ID: 通常在exec时设置
错误处理:
- EPERM: 权限不足
- EINVAL: ID值无效
- EAGAIN: 资源暂时不可用
安全考虑:
- 权限提升: 可能导致安全风险
- 审计日志: 建议记录权限变更
- 最小权限: 遵循最小权限原则
- 状态恢复: 及时恢复原始状态
最佳实践:
- 保存状态: 执行前保存原始权限状态
- 检查权限: 验证是否有足够权限
- 错误处理: 妥善处理各种错误情况
- 及时恢复: 使用完特权后及时恢复
- 日志记录: 记录权限变更操作
函数对比
函数 | 参数数量 | 功能 | 适用场景 |
---|---|---|---|
setreuid | 2个 | 设置真实和有效用户ID | 简单权限切换 |
setregid | 2个 | 设置真实和有效组ID | 简单权限切换 |
setresuid | 3个 | 设置真实、有效和保存用户ID | 完整权限控制 |
setresgid | 3个 | 设置真实、有效和保存组ID | 完整权限控制 |
总结
setresuid/setresgid/setreuid/setregid
提供了灵活的权限管理机制:
- 精确控制: 可以精确控制进程的权限状态
- 安全机制: 通过权限检查保证系统安全
- 灵活使用: 支持部分参数设置(使用-1)
- 标准兼容: 符合POSIX标准
在实际应用中,需要:
- 遵循安全最佳实践
- 妥善处理错误情况
- 及时恢复原始权限状态
- 记录权限变更日志