Как `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' в базе данных группы.