setgroups函数用法及示例

服务器繁忙,请稍后再试。

setgroups函数用法及示例

setgroups 是 Unix/Linux 系统中的系统调用函数,用于设置调用进程的附加组 ID(supplementary group IDs)。它需要特权权限(通常是 root 用户),通常与权限管理相关(如切换用户身份)。


函数原型

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

int setgroups(size_t size, const gid_t *list);
  • 参数
    • size:数组 list 中的元素数量。
    • list:指向 gid_t 类型数组的指针,包含要设置的组 ID 列表。
  • 返回值
    • 成功时返回 0
    • 失败时返回 -1 并设置 errno(常见错误:EPERM 表示无权限)。

关键说明

  1. 需要特权:只有 root 用户或有 CAP_SETGID 能力的进程可调用。
  2. 覆盖原有组:调用后会完全替换进程当前的附加组 ID 列表。
  3. 安全实践:通常在 setgid()setuid() 前调用,以彻底放弃特权。

示例代码

以下示例展示如何用 setgroups 将进程的附加组设置为 10011002,并切换至非特权用户:

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
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>

int main() {
gid_t new_groups[] = {1001, 1002}; // 目标附加组 ID
size_t num_groups = sizeof(new_groups) / sizeof(gid_t);

// 1. 设置附加组 (需 root 权限)
if (setgroups(num_groups, new_groups) == -1) {
perror("setgroups failed");
exit(EXIT_FAILURE);
}

// 2. 设置主组 ID (假设目标用户主组为 1000)
if (setgid(1000) == -1) {
perror("setgid failed");
exit(EXIT_FAILURE);
}

// 3. 设置用户 ID (假设目标用户 UID 为 1000)
if (setuid(1000) == -1) {
perror("setuid failed");
exit(EXIT_FAILURE);
}

printf("权限已切换!当前 UID=%d, GID=%d\n", getuid(), getgid());
return 0;
}

编译与运行

  1. 编译
    1
    gcc -o setgroups_example setgroups_example.c
  2. 运行(需 root)
    1
    sudo ./setgroups_example
  3. 输出示例
    1
    权限已切换!当前 UID=1000, GID=1000

常见错误

  • 权限不足EPERM):
    • 非 root 用户调用会失败。
    • 确保程序以 root 启动(如通过 sudo)。
  • 无效组 ID
    • 确保 list 中的组 ID 在 /etc/group 中存在。
  • 数组大小超限
    • size 不能超过系统限制(sysconf(_SC_NGROUPS_MAX))。

替代函数

  • initgroups(const char *user, gid_t gid)
    • 自动根据用户名设置附加组(包含 /etc/group 中的组)。
    • 更安全便捷,推荐优先使用。
1
2
3
4
// 示例:使用 initgroups 设置用户 "john" 的附加组
if (initgroups("john", 1000) == -1) {
perror("initgroups failed");
}

建议在需要精细控制组 ID 时使用 setgroups,普通场景用 initgroups 更简便。