[nSLUG] chmod +s , cupsd restart

Peter Cordes peter at llama.nslug.ns.ca
Sat Jun 21 22:46:57 ADT 2003

On Thu, Jun 19, 2003 at 05:10:40PM -0300, Donald Teed wrote:
> On Thu, 19 Jun 2003, Peter Cordes wrote:
> >  The conventional wisdom about suid wrapper programs is that you should use
> > exec(2), _not_ system(3), because system pays attention to all kinds of
> > environment variables, and so is easier to exploit (i.e. harder to use
> > securely).
> > 
> >  You could do away with the script entirely, and execl(2) killall -HUP
> > cupsd.  (or even write your own code to read /var/run/cupsd.pid and kill(2)
> > that pid.
> Thanks for the tip/suggestion.
> Here is what I tried.  It looked promising, but it
> didn't actually trigger cupsd to reset.
> I made this program:
> main()
> { execl ("killall -HUP cupsd"); }
> I compiled it, and did a chmod +s on a.out.  An ordinary
> user could run it fine.  But no reset noted in /var/log/cups/error.log.

 strace ./a.out, and you'll see the problem: 
execve("killall -HUP cupsd", ["\277\374\377\277", "x\373\377\277Q:\3@\1",
"[\201\303\360\21", "\4", "t\316\20", "", "\211\4$\350\257-\1", umovestr:
Input/output error

execl(3) doesn't tokenize the string, and it needs more arguments.
(compiling without headers prevented gcc from warning you about that.)
Besides that, execl(3) doesn't search $PATH.  You _have_ to specify a
complete path, and an argument list.

 Anyway, it's better to use system calls that are as simple as possible.
Earlier, I suggested execl (and implied it was a system call (man section
2), rather than the library function it actually is (section 3)).  execve(2)
is the underlying system call that all the exec functions use, so I'll post
a (working) example of a program that uses it:

#include <unistd.h>

int main(void)
   char *args[] = {"killall", "-HUP", "cupsd", NULL};
   char *env[] = {NULL};
   return execve("/usr/bin/killall", args, env );

strace shows that the desired system call is made:
execve("/usr/bin/killall", ["killall", "-HUP", "cupsd"], [/* 0 vars */]) = 0

(and yes, "killall" as the first argument in the array is right.  You know
how shell scripts can see how they were called by checking $0, or C programs
can look at argv[0]?  Well, that argument has to be supplied by the caller.
It's _not_ generated automatically from the path of the file being executed.)

 As Jamie said, custom made suid wrappers are not usually a good solution,
since sudo provides more hurdles to possible attackers.  If someone gets
control of a process running as a user = apache, or something like that,
sudo won't let them in.  OTOH, for killall -HUP cupsd, it hardly matters who
can run that when, so you don't really need any access control at all.  It's
probably better to use sudo than to implement uid/gid checking in your own
suid wrapper, but if you weren't going to do that, a program that _just_
runs execve(2) will be fine, AFAIK.

> If I ran a.out as root I do see a verification of
> cupsd rereading the files (as intended) in error.log

 Are you sure?  AFAICT, it shouldn't have worked.

#define X(x,y) x##y
Peter Cordes ;  e-mail: X(peter at llama.nslug.n , s.ca)

"The gods confound the man who first found out how to distinguish the hours!
 Confound him, too, who in this place set up a sundial, to cut and hack
 my day so wretchedly into small pieces!" -- Plautus, 200 BC

More information about the nSLUG mailing list