8

The various set*gid() system calls require privileges to change groups except in a very few cases. Changing primary group to one of the processes' supplementary groups does not appear to be one of them meaning the newgrp/sg commands for instance need to elevate privileges to switch primary group.

Is there a reason why setgid()/setegid()/setregid()/setfsgid() don't allow switching to a supplementary group without privs? If so what is the reason?

  • 1
    Not sure either. Note that you can chgrp a file to one of your supplementary groups, so if you have write access to an area without noexec,nosuid, you can work around that limitation (by creating a copy of /usr/bin/env with setgid permission). – Stéphane Chazelas Feb 23 '16 at 11:58
  • Thew newgrp command already does this for me AFAICT but spawning an external program isn't always what one wants to do. – William Hay Feb 23 '16 at 12:02
  • Note that newgrp/sg refers to the account database, not the supplementary group list of the process. – Stéphane Chazelas Feb 23 '16 at 12:17
  • If your gid is not also in your list of supplementary ids, then allowing setgid() would allow you to leave membership of a group (which would be a security concern), but then again you could also do it with the same setgid executable trick as above, and your gid usually also is in your supplementary list (initgroups(3) takes a gid argument just for that). – Stéphane Chazelas Feb 23 '16 at 14:14
3

Of course, the fundamental puzzle here is that filesystem permission checks are based on the combination of (the effective UID and) the effective GID and the supplementary GIDs. So, from the point of view of file permissions checks, the effective GID is equivalent to the supplementary GIDs, which leads to the OP's question. (In passing: if we are talking about Linux, it is actually the filesystem UID/GID that are used in filesystem permission checks, rather than the effective UID and GID, but the former IDs almost always have the same values as the latter IDs.)

So, there must be some cases where the real/effective/saved-set GIDs are not equivalent to the supplementary GIDs. (I group the real/effective/saved-set GIDs together, because the normal set*gid() permission rules say that an unprivileged process can change any one of those GIDs to the same value as one of the other two.)

And indeed, there are a few such cases. access(2) makes its checks based on the process's real user ID and group ID. If an unprivileged user was able to change the real group ID to be the same as one of the supplementary GIDs that is not the effective or saved set GID, then the behavior of access(2) could be manipulated.

There are other such cases. See the Linux mkdir(2) man page, for an example. Depending on whether the set-GID mode bit is set on the parent directory, a new file created in the directory takes its group ownership from the creating process's effective GID. Again, if an unprivileged process could change its effective GID to be the same as one of its supplementary GIDs, it could manipulate the group ownership of new files in unexpected ways. Similar comments apply for mknod(2) and the System V IPC calls semget(2), shmget(2), and msgget(2).

There are also some Linux-specific cases where the real/effective/saved set GIDs are not equivalent to the supplementary GIDs. See process_vm_readv(2) and prlimit(2), for example.

  • Note: @mtk is the author of The Linux Programming Interface. – Stéphane Chazelas Feb 26 '16 at 11:10
  • The effective gid determines the group ownership of new files. But then you can change that group to one of your supplementary gids afterwards (and also give the setgid bit allowing your process to effectively do a setgid()). So it seems a bit like a weak reason. – Stéphane Chazelas Feb 26 '16 at 11:15
  • @StéphaneChazelas: agreed, it's a little weak. On OTOH, that's now a two step process, which (and I'm reaching here) has the potential for weird races. But, aside from that case, there are the others I mention above. I don't know what precise reason there was for this design decision. Perhaps it was access(2), since that is an ancient part of the API. Many of the others only arrived later (or are Linux-specific) or (I presume) were not present on BSD when supplementary GIDs were added. Or, maybe, it was just about preserving historical behavior; I see you added that point as an answer. – mtk Feb 26 '16 at 16:07
3

I think the reason is primarily historical. Supplementary groups were not added until 4.2BSD (circa 1983). Before that, you only had the real and effective uids and gids.

The behaviour for setuid/setgid was completely symmetrical and had no reason not to be. You'd switch user with su, and group with sg/newgrp all setuid executables. The information about user group membership resided only in the user database, not in attributes of processes.

And the setuid/setgid interface was not changed when supplementary gids were added.

Technically now, if you have write access to a file system (where execution and setuid/setgid are not disabled), you can still set your effective or real user ID to any of your supplementary gids (without having to resort to sg/newgrp which btw only allow to change to groups defined in the user database, which is not necessarily the same as the list of supplementary gids of the process).

cp /usr/bin/env .
chgrp any-sup-group env
chmod g+s ./env

And upon executing env, your egid switches to any-sup-group.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.