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

от 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 ]

0 голосов
/ 05 сентября 2018

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

Тогда как 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 показывает открытые поля для имени пользователя 'username' в базе данных пользователей, getent group показывает открытые поля базы данных группы, а getent group groupname показывает открытые поля группы 'namename' в базе данных группы.

0 голосов
/ 05 сентября 2018

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

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

...