5

On GNU/Linux systems that are build using RPM packages, the rpmlint utility complains about programs that don't call setgroups before setuid.

The idea is that before dropping privileges, a process should also drop the list of supplementary group ID's with setgroups(0, NULL).

However, is this something that should always be done?

Suppose that we are running setuid root, and are carrying a list of supplementary group ID's from our original security context: the groups associated with the real user ID.

When we drop back to that real user ID, we don't necessarily want to lose those groups: code executing as the original user may depend on those supplementary memberships being in place, right?

Should we not omit setgroups(0, NULL) in setuid code before dropping privs to the original user?

(By the way, of course we don't drop privileges with setuid on Linux because that doesn't work for code running setuid non-root.)

Kaz
  • 2,303
  • 16
  • 17

1 Answers1

5

This almost entirely depends on whether you're temporarily or permanently dropping privileges.

In the case of temporarily dropping privileges, it's actually ill-advised to call setgroups with a single group as you'll be wiping the list of ancillary groups, potentially making it impossible (without external intervention) to restore the process back to its original privilege level.

In the case of permanently dropping privileges, it is important to clear the ancillary groups list in order to make it more difficult for an exploit to re-instate privileges after they are dropped. The problem arises when you attempt to do this the wrong way around: if you setuid to a non-zero value first (meaning you're no longer root), then call setgroups, the effective uid of the process is now no longer root, meaning that the internal setgid call fails. Doing it the other way around doesn't have this problem because calling setgid first doesn't alter the effective uid of the process, allowing setuid to be called.

More info can be found here:

Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • I'm interested specifically in the case of permanently dropping privileges, *back to the original real ID user* who invoked a setuid program. – Kaz May 02 '16 at 21:24
  • @Kaz In which case, the second half of my answer should cover what you want. Failing to drop the ancillary groups may imbue additional capabilities which would otherwise be lost when doing `setgroups` with a single non-zero gid (before `setuid` of course). – Polynomial May 02 '16 at 21:27
  • Are you trying to say that the diagnostic in `rpmlint` is purely about order? That is to say, it is saying that you have both setgroups and setuid in the program, but apparently in the wrong order? – Kaz May 02 '16 at 21:28
  • Is that because more groups may have been added? E.g. user 1 has groups 10, 11, 12. Runs setuid program, which makes it 10, 11, 12, 51. Dropping back to user 1 should restore the supplementary groups to 10, 11, 12. No? Not just clobber the list to empty. What if I control all the code that runs setuid root and know that the group ID's have not been manipulated? – Kaz May 02 '16 at 21:32
  • The "Dropping Privileges in setuid Programs" in fact is saying that the supplementary group list may be manipulated under setuid root (this is the issue), and that when returning to the original privileges, the original list may be restored. That's basically the answer: don't clobber, but restore (if there is any reason to suspect it was changed). – Kaz May 02 '16 at 21:36
  • @Kaz Not quite. Imagine you have the setgid bit on a binary, and one of those groups is given the privilege to access X11 sessions. That binary wants to drop privileges, so it calls `setuid` to the current user who ran the binary. That user isn't in the X11 group. Now that user, through that process, has access to X11 when it didn't before. Calling `setgroups` with a single group (that of the target user) removes this issue. – Polynomial May 02 '16 at 21:36
  • @Kaz Alternatively, imagine you assign an ancillary group to `root` (uid 0) to provide it with administrative privileges on a particular feature on a service which doesn't give root administrative access by default. A binary which is setuid and owned by root will now have access to that group's privilege. If it doesn't drop that group, it will drop privilege to the uid of the user it calls `setuid` on, but still have access to that group due to the ancillary groups. By calling `setgroups` first it can properly drop that additional privilege. – Polynomial May 02 '16 at 21:40
  • @Kaz A comparable case on Windows would be a process running as an administrative user which launches a thread that runs under an alternative security context via impersonation, without dropping a critical process-level privilege such as `SeDebugPrivilege`. – Polynomial May 02 '16 at 21:41
  • Are you saying the setuid mechanism (or setgid?) opens the password/group databases and installs supplementary groups (and does not just manipulate effective ID's?) – Kaz May 02 '16 at 21:42
  • @Kaz No, not at all. The process has an effective privilege set based on its stored gid and uid plus supplementary gids. When the process calls `setuid` it doesn't do anything to the gids; you have to change that yourself. By calling `setgroups` you can clear the stored gid and ancillary gids properly. Without doing that, your process still runs at the same effective gid as before. – Polynomial May 02 '16 at 21:45
  • On what platforms? I don't see anything in the Linux documentation about `setgroups` having any effect on the effective or saved gid, only the list. (experimentation confirms it). I'm afraid I don't follow most of what you're saying, but the references in your answer are useful. I wouldn't use `setgroups` for the side effect of manipulating the real/stored/effective `gids`; for that I have `setresgid`. I wouldn't use `setresgid` unless the program is running setgid. – Kaz May 02 '16 at 21:55
  • I don't understand what is achieved by calling `setgroups` with a single group, versus an empty `setgroup`; I can open a new question for that, though. (Maybe an empty `setgroups` doesn't work on some Unixes?) – Kaz May 02 '16 at 22:00