The linkfile and SUID mystery.

How does linkfile(1) know what the original user ID was, when it’s run from inside a SUID program? Well, it’s a mystery to me anyway.

Here’s an example program:

int main() {
  system("linkfile /tmp/foo.lock");
  return 0;
}

This program simply calls linkfile /tmp/foo. When I run it, it creates a file /tmp/foo.lock that belongs to me. Great. Now if I give the program to another user, and they set its SUID bit, it still creates a file owned by me when I run it! That’s crazy, because the program is now running under the other guy’s user ID. Here, I’ll prove it to you…

int main() {
  system("linkfile /tmp/foo.lock && rm -f /tmp/foo");
  return 0;
}

Now the program creates the lock file, and then removes it. This works fine if I just run it, but if I run is SUID, I get “permission denied” when it tries to remove the file. That’s because the program’s owner doesn’t have the right to delete the lockfile, because somehow it belongs to me.

Mystery Solved

I investigated what lockfile was doing by running it inside strace. Here’s the offending portion:

getuid32()                              = 501
setuid32(501)                           = 0

…and here are the corresponding lines from the source code:

uid=getuid();
if(setuid(uid)||geteuid()!=uid)   /* resist setuid operation */
{ nlog("Unable to give up special permissions");
  return EX_OSERR;
}

So there you have it. It’s deliberately programmed to give up SUID privileges before doing anything.

Leave a Comment

Sponsors