“ initgroups”如何调用“ setgroups”来初始化用户的补充组ID列表?

时间:2018-09-04 21:38:54

标签: c linux usergroups

来自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,   setgrentendgrent(我们之前已经介绍过)并确定   用户名的组成员身份。 然后调用setgroups   初始化用户的补充组ID列表

initgroups可以使用用户名作为参数,而setgroups不能将用户名作为参数。那么initgroups如何调用setgroups来初始化任意用户的补充组ID列表?

谢谢。

2 个答案:

答案 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”的公共字段。