13

Processes which de-escalate privileges via setuid() and setgid() do not seem to inherit the group memberships of the uid/gid they set.

I have a server process that must be executed as root in order to open a privileged port; after that it de-escalates to a specific non-privilleged uid/gid,1 -- e.g., that of user foo (UID 73). User foo is a member of group bar:

> cat /etc/group | grep bar
bar:x:54:foo

Hence if I login as foo, I can read a file /test.txt with these characteristics:

> ls -l /test.txt
-rw-r----- 1 root bar 10 Mar  8 16:22 /test.txt

However, the following C program (compile std=gnu99), when run root:

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

int main (void) {
    setgid(73);
    setuid(73);
    int fd = open("/test.txt", O_RDONLY);
    fprintf(stderr,"%d\n", fd);
    return 0;
}   

Always reports Permission denied. I imagine this has to do with it being a non-login process, but it kind of hamstrings the way permissions are supposed to work.


1. Which is often SOP for servers, and I think there must be a way around this as I found a report of someone doing it with apache -- apache has been added to the audio group and can apparently then use the sound system. Of course, this likely happens in a fork and not the original process, but in fact the case is the same in my context (it's a child process forked subsequent to the setuid call).

7
  • Switch the setuid()/setgid() calls around.
    – vonbrand
    Mar 8, 2014 at 22:25
  • @vonbrand ROTFL I thought I was in for a facepalm there -- but same result, so I'll edit the question to eliminate the red herring.
    – goldilocks
    Mar 8, 2014 at 22:27
  • 1
    If you use setgid(54) instead of setgid(73) (as in /etc/groups, group bar has gid 54), does it work?
    – lgeorget
    Mar 8, 2014 at 22:35
  • @lgeorget Sure, but that defeats the purpose. The process needs its own GID for other reasons, and likewise, those files must have the permissions they have. That's why membership in groups plural is necessary -- e.g., what if you have two users that need to do this. Note you can't setuid() again after you do it...but, hmmm...I think you can with seteuid()...
    – goldilocks
    Mar 8, 2014 at 22:52
  • 1
    My question was to be sure there was no other hidden subtle problem somewhere. :-)
    – lgeorget
    Mar 8, 2014 at 22:54

2 Answers 2

16

The problem is that setuid and setgid are not sufficient to give your process all the credentials it needs. The authorizations of a process depend on

  • its UID
  • its GID
  • its supplementary groups
  • its capabilities.

See man 7 credentials to get a more detailed overview. So, in your case, the problem is that you correctly set the UID and GID, but you don't set the supplementary groups of the process. And group bar has GID 54, not 73, so it is not recognized as a group your process is in.

You should do:

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

int main (void) {
    gid_t supplementary_groups[] = {54};

    setgroups(1, supplementary_groups);
    setgid(73);
    setuid(73);
    int fd = open("/test.txt", O_RDONLY);
    fprintf(stderr,"%d\n", fd);
    return 0;
}  
2
  • 2
    That was an interesting question which deserves more upvotes because it could actually be useful to a lot of people out there. :-)
    – lgeorget
    Mar 8, 2014 at 23:00
  • So I was having a similar issue with serial ports. I implemented this for the dialout group and it worked first time.
    – tl8
    Jul 11, 2017 at 6:22
0

OK, trawled around the net a bit. I thought at first that APUE would hold all the answers, but was mistaken. And my copy (old edition) is at work, so... The Unix and Linux Administration Handbook's chapter 5 looks promising, but I haven't got it (just a copy of the first two editions, also at work).

The little resources I've found (google for "daemon writing unix") all talk about important steps, like how to dissociate from the tty, etc. But nothing about UID/GID. Strangely, not even the extensive HOWTO collection at https://tldp.org/ seems to have details. Only exception is Jason Short's Let's Write a Linux Daemon - part I. Full details of how SUID/SGID and all that mess works is Chen, Wagner and Dean's SUID demystified (a paper in USENIX 2002). But be careful, Linux has an extra UID, the FSUID (see Wolter's Unix Incompatibility Notes: UID Setting Functions for a discussion).

Daemonizing a process is definitely not for the faint of heart. General security considerations are given in D. Wheeler's Secure Programming for Linux and Unix HOWTO -- Creating Secure Software. Systemd promises to simplify most of that (and thus reduce the room for mistakes that lead to security problems), see the daemon manual.

1
  • 1
    The question isn't about daemonization. You've confused the SUID bit (giving a process the permissions of the owner of its executable) with setuid(), which allows the process to change it's UID arbitrarily. SUID is usually intended to allow an escalation of permissions (non-privilleged -> privilleged), whereas setuid() can only do the opposite.
    – goldilocks
    Mar 9, 2014 at 1:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .