Search code examples
clinuxbufferingsetuidlinux-capabilities

stdbuf with setuid/capabilities


I am reading output from another process which generates output (slow and infinite). Because I want to read this data in real-time I use "stdbuf -oL" (line-buffered, data is text). I do not have control of the generating process so I cannot modify the source to force flushing.

So far stdbuf works just fine, however the process uses SOCK_RAW and needs either to be run as root, have setuid(0) or the cap_net_raw capability. When running as non-root with setuid or capabilities stdbuf seems to be ignored. Let me demonstrate the problem:

This is a simple writer:

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

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

And a simple reader:

#include <stdio.h>

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

As expected, by executing ./writer | ./reader nothing shows up until the buffer is filled. Prepending stdbuf -oL enables line-buffering and I get the lines into the reader:

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

But if I add cap_net_raw+ep it stops working:

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

The same behaviour is observed when using setuid:

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

I'm interested in understanding why this happens and how I can continue to use stdbuf without running as root. I admit that I do not fully understand what setuid is doing behind the scenes.


Solution

  • From looking at the stdbuf source code it looks like it works by setting LD_PRELOAD. There are of course security concerns using LD_PRELOAD with setuid executables or sudo.

    One suggestion I found was to disable the noatsecure selinux attribute for your executable.

    Another, simpler, option would be to avoid stdbuf and simply call fflush(stdout) from your source code directly.