Хотя Бармар уже ответил на поставленный вопрос, я думаю, что было бы полезно немного углубиться в детали.
Тогда как 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' в базе данных группы.