stdbuf с setuid/ возможностями

Я читаю вывод из другого процесса, который генерирует вывод (медленный и бесконечный). Поскольку я хочу читать эти данные в режиме реального времени, я использую "stdbuf -oL" (с буферизацией строки, данные являются текстовыми). У меня нет контроля над процессом генерации, поэтому я не могу изменить источник для принудительной очистки.

Пока что stdbuf работает просто отлично, однако процесс использует SOCK_RAW и должен быть запущен от имени пользователя root, иметь setuid(0) или cap_net_raw возможность. При запуске без полномочий root с setuid или возможностями stdbuf, похоже, игнорируется. Позвольте мне продемонстрировать проблему:

Это простой писатель:

#include <stdio.h>
#include <unistd.h>

int main(){
        int i;
        for ( i = 0;; i++){
                fprintf(stdout, "%d\n", i);
                sleep(1);
        }
}

И простой читатель:

#include <stdio.h>

int main(){
        char* line = NULL;
        size_t n = 0;
        while (getline(&line, &n, stdin) != -1 ) {
                fputs(line, stdout);
        }
}

Как и ожидалось, выполняя ./writer | ./reader ничего не появляется, пока буфер не заполнен. Предварение stdbuf -oL включает буферизацию строки, и я получаю строки в считыватель:

% stdbuf -oL ./writer | ./reader
0
1
2
...

Но если я добавлю cap_net_raw+ep перестает работать:

% sudo setcap cap_net_raw+ep ./writer
% stdbuf -oL ./writer | ./reader
(no output)

Такое же поведение наблюдается при использовании setuid:

% sudo chown root:root ./writer
% sudo chmod +s ./writer
% stdbuf -oL ./writer | ./reader
(no output)

Мне интересно понять, почему это происходит и как я могу продолжать использовать stdbuf, не работая от имени пользователя root. Я признаю, что не до конца понимаю, что setuid делает за кулисами.

2 ответа

Решение

Если посмотреть на исходный код stdbuf, похоже, что он работает, установив LD_PRELOAD. Конечно, существуют проблемы безопасности при использовании LD_PRELOAD с исполняемыми файлами setuid или sudo.

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

Другой, более простой вариант - избегать stdbuf и просто вызывать fflush(stdout) из вашего исходного кода напрямую.

Решение без LD_PRELOAD

Вы можете использовать unbuffer утилита, которая является частью expect (expect-devel) пакет. unbuffer очень короткий сценарий ожидания. Не нужно LD_PRELOAD потому что он использует другой трюк. expect создает псевдо-терминал (как xterm или же ssh) поэтому процесс выполняется с использованием unbuffer обманут, думая, что пишет на интерактивное устройство, поэтому по умолчанию он использует буферизацию строки stdout,

Использование в вашем случае:

unbuffer ./writer | ./reader

Если stdbuf работает с программой unbuffer будет работать с большой вероятностью тоже. Так как LD_PRELOAD накладывает некоторые ограничения unbuffer имеет преимущества перед stdbuf, Вопреки stdbuf он будет работать с такими исполняемыми файлами:

  • УИП
  • с файловыми возможностями
  • статически связаны
  • не используя стандарт libc
Другие вопросы по тегам