
Посмотрите на эту программу на языке C:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
printf("res=%d\n", setuid(1001));
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
return 0;
}
Моя учетная запись пользователя — «test» (id 1000). У меня есть вторая учетная запись пользователя: «test2» (id 1001).
Вот что я сделал:
gcc test.c -o ./a.out
sudo chown test2 ./a.out
sudo chmod u+s ./a.out
Теперь, вот что произойдет, если я запущу ./a.out:
UID: 1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID: 1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
Я не понимаю, почему я не вижу uid=1001 во второй части...
Я попробовал это:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
printf("res=%d\n", setuid(0)); // <- Here is the change
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
return 0;
}
gcc test.c -o ./a.out
sudo chown root ./a.out
sudo chmod u+s ./a.out
Вот что я получаю при запуске ./a.out:
UID: 1000
EUID: 0
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID: 0
EUID: 0
uid=0(root) gid=1000(test) groups=1000(test),...
Теперь это работает.
Поэтому я не понимаю, почему setuid не работает с пользователем test2, но работает с пользователем root...
Есть идеи ?
Спасибо
решение1
Здесь происходит несколько вещей, которые вызывают эту путаницу. Setuid работает в обоих случаях, просто он работает не так, как вы думаете. Во-первых, есть проблема реальных и эффективных идентификаторов пользователей. Согласностраница руководства setuid(2)
Если вызывающий процесс является привилегированным (точнее: если процесс имеет
CAP_SETUID
соответствующие возможности в своем пространстве имен пользователя), также устанавливаются реальный UID и сохраненный set-user-ID.
Это также означает, что вызывающий процесс делаетнетесть CAP_SETUID
возможность (что имеет место у ваших обычных пользователей), то UID будетнетизменить только EUID.
Итак, переходим к system()
вызову. Вызов system("anything")
— это эквивалент вызова этого:
execl("/bin/sh", "sh", "-c", "anything", (char *) NULL);
Итак, он порождает /bin/sh
, предоставляя аргументы, чтобы новая оболочка выполнила вашу команду. Я предполагаю, что вы используете bash в качестве оболочки, потому что bash работает так
Если оболочка запущена с эффективным идентификатором пользователя (группы), не равным реальному идентификатору пользователя (группы), и опция -p не указана, (...) эффективный идентификатор пользователя устанавливается равным реальному идентификатору пользователя.
Так что если ваш процесс не имеет возможности CAP_SETUID
, он не изменит реальный идентификатор пользователя, только эффективный UID. Затем, когда bash порождается, он сбрасывает эффективный UID, поскольку UID не равен EUID. Вы можете подтвердить это, вызвав
system("touch /tmp/blah")
и добавление
FILE *file = fopen("/tmp/blah2", "w+");
fclose(file);
в вашу программу. Вы увидите, что владельцем /tmp/blah
будет "test" (так как EUID будет сброшен при выполнении оболочки), но /tmp/blah2
будет принадлежать "test2".