программа setuid не работает на ядре 2.6

Мне трудно понять, почему моя программа setuid не выглядит так, как будто она на самом деле повышает разрешения, хотя идентификатор кажется правильным. Это работает на ядре 2.6 и дает сбой, но работает точно так же, как и предполагалось, в Ubuntu 14.04, делая то же самое. Мне нужна программа, которая в определенные моменты времени во время выполнения требует повышенных разрешений, а по умолчанию используется наименьшая привилегия.

#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

static uid_t _orig_euid;

void save_privilege(void){
    _orig_euid = geteuid();
    printf("saved privilege: %d\n", _orig_euid);
}

void drop_privileges(void){
    if(seteuid(getuid()) == -1){
        exit(0);
    }
    printf("dropped privileges %d %d\n", getuid(), geteuid());
}

void reacquire_privileges(void){
    if(setuid(_orig_euid) == -1){
        exit(0);
    }
    printf("reacquired privileges %d %d\n", getuid(), geteuid());
}

void do_privileged(int rw){
    switch(rw){
        case 0:
            //read from driver
            system("dd if=/dev/myrandom bs=10 count=1");
        case 1:
            //write to driver
            system("dd if=/dev/zero of=/dev/myrandom");
        case 2:
            //change something in proc fs
            system("echo 3 > /proc/sys/vm/drop_caches");
        default:
            break;
    }
}

int main(int argc, char *argv[]){
    int i;

    if(argc != 2){
        printf("usage: %s testno\n", argv[0]);
        return 0;
    }

    i = atoi(argv[1]);

    save_privilege();

    do_privileged(i);

    drop_privileges();

    do_privileged(i);

    reacquire_privileges();

    do_privileged(i);

    return 0;
}

Мои разрешения программы установлены как:

ls -l
-rwsr-xr-x    1 root     root         6547 Sep 13 00:35 test

Мой текущий идентификатор пользователя:

id
uid=1000(user) gid=1000(user)

Запись procfs, в которую я пытаюсь написать:

ls -l /proc/sys/vm/drop_caches
-rw-r--r--    1 root     root            0 Sep 13 00:36 /proc/sys/vm/drop_caches

Когда я запускаю программу, я получаю:

./test 2
saved privilege: 0
sh: cannot create /proc/sys/vm/drop_caches: Permission denied
dropped privileges 1000 1000
sh: cannot create /proc/sys/vm/drop_caches: Permission denied
reacquired privileges 1000 0
sh: cannot create /proc/sys/vm/drop_caches: Permission denied

Однако запуск той же программы в Ubuntu 14.04 работает корректно — она не может изменить запись procfs только при удалении привилегий.

Вот strace (./test_perm — это то же самое, что и ./test).

$ strace ./test_perm 2
execve("./test_perm", ["./test_perm", "2"], [/* 8 vars */]) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x40005000
open("/lib/libc.so.0", O_RDONLY)        = 3
fstat(3, {st_mode=S_IFREG|0755, st_size=310348, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x40006000
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\260\256\0\0004\0\0\0"..., 4096) = 4096
mmap2(NULL, 360448, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4000e000
mmap2(0x4000e000, 303968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 3, 0) = 0x4000e000
mmap2(0x40060000, 4972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x4a) = 0x40060000
mmap2(0x40062000, 15112, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40062000
close(3)                                = 0
munmap(0x40006000, 4096)                = 0
stat("/lib/ld-uClibc.so.0", {st_mode=S_IFREG|0755, st_size=21200, ...}) = 0
mprotect(0x40060000, 4096, PROT_READ)   = 0
mprotect(0x4000c000, 4096, PROT_READ)   = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
geteuid32()                             = 1000
write(1, "saved privilege: 1000\n", 22saved privilege: 1000
) = 22
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [], 0}, 8) = 0
vfork(sh: cannot create /proc/sys/vm/drop_caches: Permission denied
)                                 = 1183
--- SIGCHLD (Child exited) @ 0 (0) ---
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
wait4(1183, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], 0, NULL) = 1183
rt_sigaction(SIGQUIT, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
getuid32()                              = 1000
setresuid32(-1, 1000, -1)               = 0
getuid32()                              = 1000
geteuid32()                             = 1000
write(1, "dropped privileges 1000 1000\n", 29dropped privileges 1000 1000
) = 29
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_DFL, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
vfork(sh: cannot create /proc/sys/vm/drop_caches: Permission denied
)                                 = 1184
--- SIGCHLD (Child exited) @ 0 (0) ---
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
wait4(1184, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], 0, NULL) = 1184
rt_sigaction(SIGQUIT, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
setuid32(1000)                          = 0
getuid32()                              = 1000
geteuid32()                             = 1000
write(1, "reacquired privileges 1000 1000\n", 32reacquired privileges 1000 1000
) = 32
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_DFL, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
vfork(sh: cannot create /proc/sys/vm/drop_caches: Permission denied
)                                 = 1185
--- SIGCHLD (Child exited) @ 0 (0) ---
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
wait4(1185, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], 0, NULL) = 1185
rt_sigaction(SIGQUIT, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
exit(0)                                 = ?

person wireless_freedom    schedule 13.09.2016    source источник
comment
Какой следующий номер в версии 2.6? В отличие от серий ядер 3.x и 4.x, имеет смысл. В любом случае, серия ядер 2.6.x относительно старая, вы уверены, что хотите ее поддерживать? Кроме того, компиляция старого ядра под современный дистрибутив может привести к несовместимости между ядром ядра и libc. Можете ли вы выполнить ту же операцию (echo 3 > /proc/sys/vm/drop_caches) без вашей программы (используя su или sudo /bin/sh -c <command>)?   -  person Tsyvarev    schedule 13.09.2016
comment
@Tsyvarev, даже y имеет смысл в версии 2.6.x.y.   -  person 0andriy    schedule 13.09.2016
comment
Возможно, что перенаправление вывода '›' вызывает проблемы через функцию system(). Единственная неработающая команда "echo 3 ›..."? Если это так, попробуйте войти в привилегированный режим, фактически открыв файл proc через open(2) и write(2) в него.   -  person kaiwan    schedule 13.09.2016
comment
@Tsyvarev ядро ​​2.6.28.9, и это для встроенной платформы с пользовательским дистрибутивом. Определенно планирую перейти на более новые ядра в будущем, но сейчас нужно заставить это работать для существующего проекта. Выполнение sudo той же команды работает нормально, чего я не понимаю. Чтобы ответить на вопрос кайвана, это не единственная команда, которая не работает, это был всего лишь один пример, я также не могу загружать драйверы с помощью системы системных вызовов (insmod testdriver.ko), в основном все, что я могу сделать как root , я не могу сделать с программой выше.   -  person wireless_freedom    schedule 13.09.2016
comment
в общем, это утверждение: exit(0); неверно для выхода после состояния ошибки. Это потому, что 0 является показателем успеха. Предложите использовать (из stdlib.h) exit( EXIT_FAILURE );   -  person user3629249    schedule 15.09.2016
comment
оператор switch() написан неправильно. В конце каждого случая должен быть оператор break;.   -  person user3629249    schedule 15.09.2016
comment
устройство /dev/myrandom не является частью ОС, вы сами его создали? Какие разрешения/свойства у него есть?   -  person user3629249    schedule 15.09.2016
comment
относительно этой строки: system("dd if=/dev/zero of=/dev/myrandom"); /dev/zero предоставит только запрошенное количество символов NUL. К сожалению, в командной строке отсутствует параметр count=, поэтому этот вызов может никогда не завершиться.   -  person user3629249    schedule 15.09.2016
comment
Чтобы освободить slab-объекты и кэш страниц: echo 3 > /proc/sys/vm/drop_caches Этот файл не является средством управления ростом различных кэшей ядра (иноды, dentries, кэш страниц и т. д.). Эти объекты автоматически освобождаются ядром, когда память требуется в другом месте на система. Использование этого файла может вызвать проблемы с производительностью. Поскольку он отбрасывает кэшированные объекты, повторное создание отброшенных объектов может потребовать значительного объема операций ввода-вывода и ЦП, особенно если они интенсивно использовались. По этой причине не рекомендуется использовать его вне среды тестирования или отладки.   -  person user3629249    schedule 15.09.2016


Ответы (1)


Вызов вашей программы под strace приводил к игнорированию бита setuid.

Вы пытались запустить двоичные файлы setuid с ruid ==, вызвав идентификатор пользователя. Это не очень хорошо работает; однако это тоже не кажется вашей главной проблемой.

Не никогда не вызывайте system() из программы setuid, иначе кто-нибудь SHELL=/tmp/evil your_setuid_program сделает это и получит root на завтрак.

Я не решаюсь даже выяснить, что вы действительно делаете неправильно, из-за чего бит setuid не работает для вас, потому что вы, очевидно, понятия не имеете, как писать двоичные файлы setuid.

person Community    schedule 13.09.2016
comment
хорошо, у меня есть приложение, работающее во встроенном Linux, которое должно иметь возможность выполнять вызовы system(), но уменьшать привилегии, когда вызовы system() не нужны, все, что я читал, говорит, сделайте его программой setuid, немедленно уменьшите привилегии в запустить приложение, затем повторно получить привилегии root только при необходимости (когда мне нужно выполнить вызовы system()), а затем снова отказаться от привилегий, у вас есть лучшее предложение о том, как это сделать? - person wireless_freedom; 13.09.2016
comment
@wireless_freedom: Попробуйте написать программу, которая запускается от имени пользователя root и делает именно то, что ей нужно, без вызова system(), и тогда мы сможем поговорить. Если вы не можете этого сделать, в этом просто нет смысла. Для всех ваших примеров, кроме insmod, вы вообще не должны запускать какие-либо программы. - person Joshua; 13.09.2016