2

I wrote a simple program as follows -

int main(int argc, char* argv[]) {
    setuid(0); 
    setgid(0);
    printf("Current uid and euid are %d, %d\n", getuid(), geteuid());
    while(1);
}

I compiled this as root and set the setuid bit using sudo chmod +s test.

When this program is run as a non-privileged user from bash, the program prints -

Current uid and euid are 0, 0

and then gets stuck in an infinite loop.

However I can still kill this process by pressing Crl+C. If I understand correctly, bash(running as a non-privileged user) should not be able to send SIGINT to a root process.

I also tried the same with kill <pid of test> and that fails as excepted.

How is bash able to kill the process? Is there a special relationship between the parent process and the child process?

I also tried this other wrapper program -

int main(int argc, char* argv[]) {
        pid_t p = fork();
        if (p == 0) {
                char * args[] = {"./test", NULL};
                execv("./test", args);
        } else {
                sleep(4);
                int ret = kill(p, 9);
                printf("Kill returned = %d\n", ret);
                return 0;
        }
}

And ran it as an unprivileged user (where test has setuid bit set by root). In this case the parent is not able to kill the child. the kill call returns -1 and the test process gets orphaned.

What is happening here? What does bash do special that it can kill the children processes it spawns?

Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49

1 Answers1

3

Bash doesn't need any permissions because bash isn't doing anything. When you hit ^C, SIGINT is sent to all processes in the foreground process group by the tty driver. The signal comes from the system, not from another process, so the permission checks relevant to one process sending a signal to another don't apply.

hobbs
  • 223,387
  • 19
  • 210
  • 288
  • I see, that makes a lot more sense. Just as an aside, I am testing this over `ssh`. Does `sshd` simply hand over the ^C to the server tty driver in this case? Or here the `sshd` (running as root) directly sends the signal? – Ajay Brahmakshatriya Nov 11 '20 at 06:14
  • 1
    sshd has connected the remote process to a psuedo-terminal. Run `tty`, it should be something like `/dev/pts/1`. That is the terminal, on the remote machine, that is responding to the interrupt character and sending a signal to the foreground process group. Using `tcsetattr(3)` you can turn this off or change the character. – TrentP Nov 11 '20 at 07:31
  • @AjayBrahmakshatriya no, the ssh server just writes the `^C` character to the master side of the pseudo-tty it had allocated on the remote machine. –  Nov 11 '20 at 12:13
  • @TrentP that makes sense. Thanks – Ajay Brahmakshatriya Nov 11 '20 at 22:01