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
ничего не отображается до заполнения буфера. Prepending 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 за кулисами.
Ответы
Ответ 1
От взгляда на исходный код stdbuf похоже, что он работает, установив LD_PRELOAD. Конечно, проблемы безопасности связаны с LD_PRELOAD с исполняемыми файлами setuid или sudo.
Одно из моих предложений заключалось в том, чтобы отключить атрибут noatsecure selinux для вашего исполняемого файла.
Другим, более простым вариантом будет избежать stdbuf и просто вызвать fflush(stdout)
из исходного кода.
Ответ 2
Решение без LD_PRELOAD
Вы можете использовать утилиту unbuffer
, которая является частью expect
(expect-devel
). unbuffer
- очень короткий ожидаемый script. Он не нуждается в LD_PRELOAD
, потому что он использует другой трюк. expect
создает псевдотерминал (например, xterm
или ssh
), поэтому процесс, выполняемый с помощью unbuffer
, обманывается, думая, что он пишет интерактивное устройство, поэтому по умолчанию он использует буферизацию строки на stdout
.
Использование в вашем случае:
unbuffer ./writer | ./reader
Если stdbuf
работает с программой unbuffer
, она также будет работать с высокой вероятностью. Поскольку LD_PRELOAD
создает некоторые ограничения, unbuffer
имеет преимущества перед stdbuf
. В отличие от stdbuf
он будет работать с этими типами исполняемых файлов:
- УИП
- с возможностями файла
- статически связанный
- не используется стандартный
libc