setuid не работает со стандартной учетной записью пользователя

setuid не работает со стандартной учетной записью пользователя

Посмотрите на эту программу на языке 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".

Связанный контент