Как `initgroups` может вызвать`setgroups` для инициализации списка идентификаторов дополнительных групп для пользователя?

От APUE

#include <grp.h> /* on Linux */
int setgroups(int ngroups, const gid_t grouplist[]);

setgroups суперпользователь может вызывать функцию для установки списка идентификаторов дополнительных групп для вызывающего процесса

#include <grp.h> /* on Linux and Solaris */
int initgroups(const char *username, gid_t basegid);

setgroups функция обычно вызывается из initgroups функция, которая читает весь файл группы - с функциями getgrent, setgrent, а также endgrent, который мы описали ранее - и определяет членство в группе для имени пользователя. Затем звонит setgroups инициализировать список идентификаторов дополнительных групп для пользователя.

initgroups может принять имя пользователя в качестве параметра, в то время как setgroups не принимает имя пользователя в качестве параметра. Тогда как можно initgroups вызов setgroups инициализировать список идентификаторов дополнительных групп для произвольного пользователя?

Благодарю.

2 ответа

setgroups() работает с текущим процессом, а не с пользователем. initgroups() получает имя пользователя в качестве параметра, ищет группы пользователей, а затем передает этот список групп setgroups() изменить список дополнительных групп текущего процесса.

Обычно это делается во время входа в систему, имя пользователя - это имя, под которым вы входите. login процесс устанавливает свой список групп, а затем исполняет вашу оболочку входа в систему. Список групп наследуется всеми другими процессами в вашем сеансе входа в систему.

Хотя Бармар уже ответил на поставленный вопрос, я думаю, что было бы полезно немного углубиться в детали.

Тогда как initgroups может вызывать setgroups для инициализации списка идентификаторов дополнительных групп для произвольного пользователя?

initgroups() сканирует базу данных группы (используя getgrent() или аналогичное внутреннее средство), чтобы составить список дополнительных групп для установки с использованием setgroups(),

Другими словами, setgroups() интерфейс для управления идентификаторами дополнительных групп текущего процесса initgroups() является вспомогательной функцией, которая сканирует базу данных группы, чтобы создать список всех идентификаторов группы, членом которой является указанный пользователь, и вызывает setgroups() установить этот набор в качестве дополнительных идентификаторов групп.

Вот пример реализации 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() ниже перечислены функции, которые управляют массивом идентификаторов групп.

На практике, когда привилегированный (корневой) процесс отбрасывает привилегии и переключается на идентификацию конкретного пользователя, он устанавливает идентификатор пользователя и группы, как указано в базе данных паролей, и идентификаторы дополнительных групп, как указано в базе данных группы. На практике такая функция чем-то похожа на

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;
}

При отбрасывании привилегий необходимо учитывать и другие важные детали (в частности, проверки доступа к файлам и устройствам обычно выполняются только во время открытия, поэтому утечка привилегированных открытых файлов является проблемой), но приведенный выше примерный набросок того, как пользователь и групповая идентичность, часть этого работает.

Обратите внимание, что вы можете использовать id утилита (входит в Coreutils, поэтому должна быть установлена ​​во всех системах) для проверки подлинности текущего процесса. Например, id -un показывает имя пользователя, соответствующее текущему идентификатору пользователя, id -gn показывает имя группы, соответствующее текущему идентификатору группы, и id -Gn перечисляет имена групп, соответствующие дополнительным идентификаторам групп.

Точно так же вы можете использовать getent утилита (устанавливается как часть библиотеки C) для проверки баз данных пользователей и паролей: getent passwd показывает открытые поля пользовательской базы данных, getent passwd username показывает открытые поля для имени пользователя в базе данных пользователей, getent group показывает открытые поля базы данных группы, и getent group groupname показывает открытые поля группы 'groupname' в базе данных группы.

Другие вопросы по тегам