Why do I need setuid(0) within a setuid-root C program that calls an administrative program with system()?

JCCyC picture JCCyC · Jun 26, 2009 · Viewed 8.3k times · Source

I had to do a dirty Linux hack for somebody so they could start a printer with the cupsenable printername shell command while being a non-root user. I didn't want them to be able to use the entirety of the cupsenable syntax as root, so I just wrote a C wrapper that sanitizes the input in argv[1] and calls system("cupsenable sanitizedprintername").

I made the program setuid root, but even so, cupsenable failed with "permission denied". Then I inserted a setuid(0) call before system() and, lo and behold, it worked.

Disregard the issue of there being a better way to give users control of the printer. There probably is a better way. What I'm interested in are the intricacies of chmod u+s vs. setuid(0) vs. system(). Why did it behave that way?

Answer

John Kugelman picture John Kugelman · Jun 26, 2009

From man system:

Do not use system() from a program with set-user-ID or set-group-ID privileges, because strange values for some environment variables might be used to subvert system integrity. Use the exec(3) family of functions instead, but not execlp(3) or execvp(3). system() will not, in fact, work properly from programs with set-user-ID or set-group-ID privileges on systems on which /bin/sh is bash version 2, since bash 2 drops privileges on startup.

And from man bash:

If the shell is started with the effective user (group) id not equal to the real user (group) id, and the -p option is not supplied, no startup files are read, shell functions are not inherited from the environment, the SHELLOPTS variable, if it appears in the environment, is ignored, and the effective user id is set to the real user id.

It appears your setuid(0) call circumvented that protection.