getresgid系统调用及示例

getresgid - 获取进程的真实、有效和保存的组ID

1. 函数介绍

getresgid 是一个 Linux 系统调用,用于同时获取当前进程的真实组 ID(Real Group ID)、有效组 ID(Effective Group ID)和保存的设置组 ID(Saved Set-group-ID)。

2. 函数原型

1
2
3
4
5
#include <unistd.h>
#include <sys/types.h>

int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);

3. 功能对比

函数功能参数getresuid(ruid, euid, suid)获取用户 ID 三元组3个 uid_t* 指针getresgid(rgid, egid, sgid)获取组 ID 三元组3个 gid_t* 指针

4. 参数说明

getresuid 参数:

  • uid_t *ruid: 指向存储真实用户 ID 的变量的指针

  • uid_t *euid: 指向存储有效用户 ID 的变量的指针

  • uid_t *suid: 指向存储保存的设置用户 ID 的变量的指针

getresgid 参数:

  • gid_t *rgid: 指向存储真实组 ID 的变量的指针

  • gid_t *egid: 指向存储有效组 ID 的变量的指针

  • gid_t *sgid: 指向存储保存的设置组 ID 的变量的指针

5. 返回值

  • 成功时:返回 0

  • 失败时:返回 -1,并设置 errno

6. 常见 errno 错误码

  • EFAULT: 指针参数指向无效内存地址

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

  • getuid(), geteuid(): 分别获取真实和有效用户 ID

  • getgid(), getegid(): 分别获取真实和有效组 ID

  • setresuid(), setresgid(): 设置用户/组 ID 三元组

  • setreuid(), setregid(): 设置真实和有效 ID

  • setuid(), setgid(): 设置用户/组 ID

  • seteuid(), setegid(): 设置有效用户/组 ID

8. 示例代码

示例1:基本使用 - 获取完整的 ID 信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <string.h>

void print_user_info(const char *label, uid_t uid) {
printf("%-20s %d", label, uid);

struct passwd *pwd = getpwuid(uid);
if (pwd != NULL) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");
}

void print_group_info(const char *label, gid_t gid) {
printf("%-20s %d", label, gid);

struct group *grp = getgrgid(gid);
if (grp != NULL) {
printf(" (%s)", grp->gr_name);
}
printf("\n");
}

int main() {
uid_t ruid, euid, suid;
gid_t rgid, egid, sgid;
int ret;

printf("=== 进程完整身份信息 ===\n");

// 获取用户 ID 三元组
ret = getresuid(&ruid, &euid, &suid);
if (ret == -1) {
perror("getresuid 失败");
exit(EXIT_FAILURE);
}

printf("用户 ID 信息:\n");
print_user_info("真实用户 ID:", ruid);
print_user_info("有效用户 ID:", euid);
print_user_info("保存的设置 UID:", suid);

// 获取组 ID 三元组
ret = getresgid(&rgid, &egid, &sgid);
if (ret == -1) {
perror("getresgid 失败");
exit(EXIT_FAILURE);
}

printf("\n组 ID 信息:\n");
print_group_info("真实组 ID:", rgid);
print_group_info("有效组 ID:", egid);
print_group_info("保存的设置 GID:", sgid);

// 分析身份状态
printf("\n身份状态分析:\n");

if (euid == 0) {
printf("✓ 当前进程具有 root 用户权限\n");
}

if (egid == 0) {
printf("✓ 当前进程具有 root 组权限\n");
}

if (ruid != euid) {
printf("✓ 用户身份已被切换 (真实: %d, 有效: %d)\n", ruid, euid);
}

if (rgid != egid) {
printf("✓ 组身份已被切换 (真实: %d, 有效: %d)\n", rgid, egid);
}

if (suid != euid) {
printf("✓ 保存的设置 UID 可用于权限恢复 (%d -> %d)\n", suid, euid);
}

if (sgid != egid) {
printf("✓ 保存的设置 GID 可用于权限恢复 (%d -> %d)\n", sgid, egid);
}

return 0;
}

示例2:错误处理和权限分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>

void safe_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) {
if (getresuid(ruid, euid, suid) == -1) {
printf("getresuid 失败: %s\n", strerror(errno));
*ruid = *euid = *suid = (uid_t)-1;
}
}

void safe_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) {
if (getresgid(rgid, egid, sgid) == -1) {
printf("getresgid 失败: %s\n", strerror(errno));
*rgid = *egid = *sgid = (gid_t)-1;
}
}

void analyze_privilege_status() {
uid_t ruid, euid, suid;
gid_t rgid, egid, sgid;

printf("=== 权限状态详细分析 ===\n");

safe_getresuid(&ruid, &euid, &suid);
safe_getresgid(&rgid, &egid, &sgid);

printf("用户权限分析:\n");
printf(" 真实 UID: %d\n", ruid);
printf(" 有效 UID: %d", euid);
if (euid == 0) printf(" (ROOT 权限)");
printf("\n");
printf(" 保存 SUID: %d", suid);
if (suid == 0) printf(" (保存 ROOT 权限)");
printf("\n");

printf("\n组权限分析:\n");
printf(" 真实 GID: %d\n", rgid);
printf(" 有效 GID: %d", egid);
if (egid == 0) printf(" (ROOT 组权限)");
printf("\n");
printf(" 保存 SGID: %d", sgid);
if (sgid == 0) printf(" (保存 ROOT 组权限)");
printf("\n");

// 权限切换能力分析
printf("\n权限切换能力:\n");

if (suid != euid) {
printf("✓ 可以通过 setuid() 恢复到保存的 UID %d\n", suid);
}

if (sgid != egid) {
printf("✓ 可以通过 setgid() 恢复到保存的 GID %d\n", sgid);
}

// 特权状态
if (euid == 0 || egid == 0) {
printf("⚠ 当前进程具有特权权限,注意安全操作\n");
}

if ((euid != ruid || egid != rgid) && (suid == ruid && sgid == rgid)) {
printf("✓ 可以通过保存的 ID 完全恢复到原始身份\n");
}
}

void demonstrate_invalid_pointer_handling() {
printf("\n=== 无效指针处理测试 ===\n");

// 测试 NULL 指针
if (getresuid(NULL, NULL, NULL) == -1) {
if (errno == EFAULT) {
printf("✓ 正确处理了 NULL 指针 (EFAULT)\n");
} else {
printf("✗ 意外错误: %s\n", strerror(errno));
}
}

// 测试部分 NULL 指针
uid_t ruid, euid;
if (getresuid(&ruid, &euid, NULL) == -1) {
if (errno == EFAULT) {
printf("✓ 正确处理了部分 NULL 指针\n");
}
}
}

int main() {
analyze_privilege_status();
demonstrate_invalid_pointer_handling();
return 0;
}

示例3:权限切换和恢复演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>

void print_current_ids(const char *context) {
uid_t ruid, euid, suid;
gid_t rgid, egid, sgid;

if (getresuid(&ruid, &euid, &suid) == -1 ||
getresgid(&rgid, &egid, &sgid) == -1) {
printf("获取 ID 信息失败\n");
return;
}

printf("%s:\n", context);
printf(" UID: %d/%d/%d (真实/有效/保存)\n", ruid, euid, suid);
printf(" GID: %d/%d/%d (真实/有效/保存)\n", rgid, egid, sgid);
printf("\n");
}

int main() {
printf("=== 权限切换和恢复演示 ===\n");

// 初始状态
print_current_ids("初始状态");

// 检查是否具有特权权限
uid_t euid;
getresuid(NULL, &euid, NULL);

if (euid != 0) {
printf("注意: 当前进程不是 root,某些权限操作可能失败\n");
printf("建议以 root 权限运行此演示\n");
}

// 演示 setuid/setgid 的效果
printf("尝试进行权限操作...\n");

// 如果是 root,可以进行权限切换演示
if (euid == 0) {
// 切换到 nobody 用户(假设 UID 65534)
uid_t nobody_uid = 65534;
gid_t nogroup_gid = 65534;

printf("尝试切换到 nobody 用户...\n");

if (setresuid(nobody_uid, nobody_uid, nobody_uid) == 0 &&
setresgid(nogroup_gid, nogroup_gid, nogroup_gid) == 0) {
print_current_ids("切换到 nobody 后");
printf("✓ 成功切换到 nobody 用户\n");
} else {
printf("✗ 切换失败: %s\n", strerror(errno));
}

// 尝试恢复权限(应该失败,因为保存的 ID 已改变)
if (setuid(0) == -1) {
printf("✓ 无法恢复到 root 权限(保存的 ID 已改变)\n");
}
} else {
printf("跳过权限切换演示(需要 root 权限)\n");
}

// 显示最终状态
print_current_ids("最终状态");

return 0;
}

示例4:安全审计和监控工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>

typedef struct {
uid_t ruid, euid, suid;
gid_t rgid, egid, sgid;
time_t timestamp;
pid_t pid;
} identity_snapshot_t;

int capture_identity_snapshot(identity_snapshot_t *snapshot) {
snapshot->pid = getpid();
snapshot->timestamp = time(NULL);

if (getresuid(&snapshot->ruid, &snapshot->euid, &snapshot->suid) == -1 ||
getresgid(&snapshot->rgid, &snapshot->egid, &snapshot->sgid) == -1) {
return -1;
}

return 0;
}

void print_identity_snapshot(const identity_snapshot_t *snapshot) {
char time_str&#91;32];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S",
localtime(&snapshot->timestamp));

printf("时间: %s\n", time_str);
printf("进程: %d\n", snapshot->pid);
printf("用户 ID: %d/%d/%d (真实/有效/保存)\n",
snapshot->ruid, snapshot->euid, snapshot->suid);
printf("组 ID: %d/%d/%d (真实/有效/保存)\n",
snapshot->rgid, snapshot->egid, snapshot->sgid);

// 显示用户名和组名
struct passwd *pwd = getpwuid(snapshot->ruid);
if (pwd) printf("真实用户: %s\n", pwd->pw_name);

pwd = getpwuid(snapshot->euid);
if (pwd) printf("有效用户: %s\n", pwd->pw_name);

struct group *grp = getgrgid(snapshot->rgid);
if (grp) printf("真实组: %s\n", grp->gr_name);

grp = getgrgid(snapshot->egid);
if (grp) printf("有效组: %s\n", grp->gr_name);
}

void security_audit() {
identity_snapshot_t snapshot;

printf("=== 安全审计报告 ===\n");

if (capture_identity_snapshot(&snapshot) == -1) {
printf("获取身份信息失败\n");
return;
}

print_identity_snapshot(&snapshot);

// 安全检查
printf("\n安全检查结果:\n");

// 检查特权权限
if (snapshot.euid == 0) {
printf("⚠ 警告: 进程以 root 权限运行\n");
}

if (snapshot.egid == 0) {
printf("⚠ 警告: 进程具有 root 组权限\n");
}

// 检查权限不一致
if (snapshot.ruid != snapshot.euid) {
printf("ℹ 信息: 用户权限已被切换\n");
}

if (snapshot.rgid != snapshot.egid) {
printf("ℹ 信息: 组权限已被切换\n");
}

// 检查保存的权限
if (snapshot.suid == 0 && snapshot.euid != 0) {
printf("ℹ 信息: 保存了 root 用户权限,可用于恢复\n");
}

if (snapshot.sgid == 0 && snapshot.egid != 0) {
printf("ℹ 信息: 保存了 root 组权限,可用于恢复\n");
}

// 检查潜在安全风险
if ((snapshot.ruid != snapshot.euid || snapshot.rgid != snapshot.egid) &&
(snapshot.suid == snapshot.ruid && snapshot.sgid == snapshot.rgid)) {
printf("✓ 安全: 可以完全恢复到原始身份\n");
}
}

void monitor_privilege_changes() {
printf("\n=== 权限变化监控 ===\n");

identity_snapshot_t initial, current;

if (capture_identity_snapshot(&initial) == -1) {
printf("无法获取初始身份信息\n");
return;
}

printf("监控 5 秒钟内的权限变化...\n");

for (int i = 0; i < 5; i++) {
sleep(1);

if (capture_identity_snapshot(&current) == 0) {
// 检查是否有变化
if (current.ruid != initial.ruid ||
current.euid != initial.euid ||
current.suid != initial.suid ||
current.rgid != initial.rgid ||
current.egid != initial.egid ||
current.sgid != initial.sgid) {

printf("检测到权限变化:\n");
printf("之前: UID(%d/%d/%d) GID(%d/%d/%d)\n",
initial.ruid, initial.euid, initial.suid,
initial.rgid, initial.egid, initial.sgid);
printf("现在: UID(%d/%d/%d) GID(%d/%d/%d)\n",
current.ruid, current.euid, current.suid,
current.rgid, current.egid, current.sgid);

// 更新初始状态
initial = current;
}
}
}

printf("监控结束\n");
}

int main() {
security_audit();
monitor_privilege_changes();
return 0;
}

9. ID 类型说明

Unix/Linux 系统中的三类 ID:

1
2
3
4
5
6
7
8
9
10
// 用户 ID 三元组
ruid // Real User ID: 启动进程的用户
euid // Effective User ID: 当前权限检查使用的用户 ID
suid // Saved Set-user-ID: 保存的设置用户 ID

// 组 ID 三元组
rgid // Real Group ID: 启动进程的组
egid // Effective Group ID: 当前权限检查使用的组 ID
sgid // Saved Set-group-ID: 保存的设置组 ID

10. 实际应用场景

场景1:权限管理工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int can_drop_privileges_completely() {
uid_t ruid, euid, suid;
gid_t rgid, egid, sgid;

if (getresuid(&ruid, &euid, &suid) == -1 ||
getresgid(&rgid, &egid, &sgid) == -1) {
return 0;
}

// 检查是否可以完全丢弃特权
return (ruid == euid && euid == suid &&
rgid == egid && egid == sgid);
}

场景2:安全审计

1
2
3
4
5
6
7
8
9
void audit_process_privileges() {
identity_snapshot_t snapshot;
if (capture_identity_snapshot(&snapshot) == 0) {
if (snapshot.euid == 0) {
syslog(LOG_WARNING, "进程 %d 以 root 权限运行", snapshot.pid);
}
}
}

场景3:权限恢复

1
2
3
4
5
6
7
8
9
10
11
12
13
int restore_original_privileges() {
uid_t ruid, euid, suid;
gid_t rgid, egid, sgid;

if (getresuid(&ruid, &euid, &suid) == -1 ||
getresgid(&rgid, &egid, &sgid) == -1) {
return -1;
}

// 恢复到原始身份
return setresuid(ruid, ruid, ruid) || setresgid(rgid, rgid, rgid);
}

11. 注意事项

使用 getresuid 和 getresgid 时需要注意:

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

指针有效性: 所有指针参数必须指向有效的内存地址

错误处理: 虽然很少失败,但仍需检查返回值

权限检查: 获取其他进程的 ID 信息可能需要权限

并发安全: 在多线程环境中注意数据一致性

系统兼容: 在所有现代 Unix/Linux 系统中都可用

总结

getresuid 和 getresgid 是管理进程身份信息的重要函数:

关键特性:

完整信息: 一次性获取所有相关的 ID 信息

原子操作: 保证获取的 ID 组是一致的

安全相关: 是权限管理和安全审计的基础

系统调用: 直接访问内核信息,性能良好

主要应用:

安全审计和监控工具

权限管理和切换程序

系统管理和调试工具

容器和虚拟化环境中的权限控制

正确理解和使用这些函数对于编写安全、可靠的 Unix/Linux 程序至关重要,特别是在需要进行权限管理和安全检查的场景中。

getresgid 是 Linux 系统调用,用于获取进程的组 ID 三元组(真实组 ID、有效组 ID 和保存的组 ID)。该函数通过三个 gid_t* 参数返回 ID 值,成功时返回 0,失败返回 -1 并设置 errno。典型用法是检查进程权限状态,常与 getresuid 配合使用。示例代码展示了如何获取并分析这些 ID,包括错误处理和权限切换能力检测。该函数在需要精细控制进程权限时非常有用,特别是在特权程序设计中。

getresgid系统调用及示例-CSDN博客

data-ad-format="auto" data-full-width-responsive="true">