setresgid/setresuid/setreuid 函数详解
1. 函数介绍
这三个函数是Linux系统调用,用于设置进程的用户ID和组ID。它们提供了更灵活的权限控制机制,允许同时设置真实ID、有效ID和保存的ID。了解setresgid/setresuid/setreuid系统调用及示例,掌握Linux进程权限管理技巧。
关键词:setresgid setresuid setreuid 系统调用详解, setresgid setresuid setreuid 函数用法, linux setresgid setresuid setreuid 示例, 系统调用 setresgid setresuid setreuid 说明, setresgid setresuid setreuid 功能解释, linux 系统调用 setresgid setresuid setreuid, setresgid setresuid setreuid 使用教程, setresgid setresuid setreuid 参数说明, linux 中 setresgid setresuid setreuid 应用, 系统调用 setresgid setresuid setreuid 示例代码
- 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标准
在实际应用中,需要:
- 遵循安全最佳实践
- 妥善处理错误情况
- 及时恢复原始权限状态
- 记录权限变更日志