来自APUE
#include <grp.h> /* on Linux */ int setgroups(int ngroups, const gid_t grouplist[]);
超级用户可以调用
setgroups
函数来设置 呼叫过程的补充组ID列表#include <grp.h> /* on Linux and Solaris */ int initgroups(const char *username, gid_t basegid);
通常从
setgroups
函数中调用initgroups
函数, 它读取整个组文件-具有功能getgrent
,setgrent
和endgrent
(我们之前已经介绍过)并确定 用户名的组成员身份。 然后调用setgroups
初始化用户的补充组ID列表。
initgroups
可以使用用户名作为参数,而setgroups
不能将用户名作为参数。那么initgroups
如何调用setgroups
来初始化任意用户的补充组ID列表?
谢谢。
答案 0 :(得分:3)
setgroups()
在当前进程上操作,而不是用户。 initgroups()
获取用户名作为参数,查找用户的组,然后将该组列表传递给setgroups()
,以修改当前进程的补充组列表。
通常在登录时完成,用户名是您登录时使用的名称。 login
进程设置其组列表,然后执行您的登录Shell。组列表由登录会话中的所有其他进程继承。
答案 1 :(得分:1)
虽然Barmar already answered提出了这个问题,但我认为稍微详细一点可能会有用。
然后,initgroups如何调用setgroups初始化任意用户的补充组ID列表?
initgroups()
扫描组数据库(使用getgrent()
或类似的内部工具)以构造要使用setgroups()
进行设置的补充组列表。
换句话说,setgroups()
是用于操纵当前进程的补充组ID的接口。 initgroups()
是一个帮助程序功能,它扫描组数据库以构造指定用户所属的所有组ID的列表,并调用setgroups()
安装该组作为补充组ID。>
这是initgroups()
的示例实现:
int initgroups(const char *name, gid_t group)
{
gidarray gids = GIDARRAY_INIT;
struct group *gr;
size_t i;
/* Initialize the gids list to the specified group. */
if (gidarray_add(&gids, group)) {
errno = ENOMEM;
return -1;
}
/* Loop through the group database. */
setgrent();
while (1) {
errno = 0;
gr = getgrent();
if (!gr) {
/* End of groups, or an error? */
if (errno) {
const int saved_errno = errno;
gidarray_free(&gids);
endgrent();
errno = saved_errno;
return -1;
}
/* No error, just end of groups. */
break;
}
/* Is there is no member list, this group is not interesting. */
if (!gr->gr_mem)
continue;
/* Check if the user is listed in this group member list. */
for (i = 0; gr->gr_mem[i] != NULL; i++) {
if (!strcmp(gr->gr_mem[i], name)) {
/* Yes; add to list, break out of this for loop. */
if (gidarray_add(&gids, gr->gr_gid)) {
gidarray_free(&gids);
endgrent();
errno = ENOMEM;
return -1;
}
break;
}
}
}
endgrent();
/* Set the supplementary group list. */
if (setgroups(gidarray_size(&gids), gidarray_ptr(&gids)) == -1) {
const int saved_errno = errno;
gidarray_free(&gids);
errno = saved_errno;
return -1;
}
gidarray_free(&gids);
return 0;
}
typedef struct {
size_t max;
size_t num;
gid_t *gid;
} gidarray;
#define GIDARRAY_INIT { 0, 0, NULL }
static void gidarray_free(gidarray *garr)
{
if (garr) {
free(garr->gid);
garr->max = 0;
garr->num = 0;
garr->gid = NULL;
}
}
static size_t gidarray_size(gidarray *garr)
{
return (garr) ? garr->num : 0;
}
static gid_t *gidarray_ptr(gidarray *garr)
{
return (garr) ? garr->gid : NULL;
}
static int gidarray_add(gidarray *garr, const gid_t gid)
{
/* Check if already included. */
size_t i = garr->num;
while (i-->0)
if (garr->gid[i] == gid)
return 0;
if (garr->num >= garr->max) {
size_t max = (garr->num | 15) + 17;
void *tmp;
tmp = realloc(garr->gid, max * sizeof garr->gid[0]);
if (!tmp)
return -1;
garr->gid = tmp;
garr->max = max;
}
garr->gid[garr->num++] = gid;
return 0;
}
gidarray_free()
,gidarray_add()
,gidarray_size()
和gidarray_ptr()
是上面功能下方列出的帮助程序功能,用于管理组ID数组。
实际上,当特权(根)进程放弃特权并切换到特定用户的身份时,它将设置password database中指定的用户ID和组ID,以及在group database中指定的补充组ID。 id
。实际上,这种功能类似于
int drop_privileges(const char *username)
{
struct passwd *pw;
/* Find out the user and group ID. */
pw = getpwnam(username);
if (!pw) {
errno = ENOENT; /* For "no such user" */
return -1;
}
/* Initialize supplementary groups. */
if (initgroups(username, pw->pw_gid) == -1)
return -1;
/* Set real, effective, and saved group ID. */
if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
return -1;
/* Omitted: Dropping Linux capabilities. */
/* Drop privileges by setting real, effective, and saved user ID. */
if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
return -1;
/* Now this process has the identity and thus privileges
of user 'username', and no more. */
return 0;
}
在删除特权时,还需要考虑其他相关细节(特别是,文件和设备的访问检查通常仅在打开时进行,因此泄漏特权打开的文件是一个问题),但是以上只是一个粗略的概述用户和组标识部分的工作方式。
请注意,您可以使用getent
实用程序(Coreutils的一部分,因此应安装在所有系统上)检查当前进程的身份。例如,id -un
显示与当前用户ID匹配的用户名,id -gn
显示与当前组ID匹配的组名,id -Gn
列出与补充组ID匹配的组名。 / p>
类似地,您可以使用{{3}}实用程序(作为C库的一部分安装)来检查用户和密码数据库:getent passwd
显示用户数据库的公共字段,{{1} }显示用户数据库中用户“ username”的公共字段,getent passwd username
显示组数据库的公共字段,getent group
显示组数据库中的组“ groupname”的公共字段。