| /* Dropping uid/gid privileges of the current process permanently. | |
| Copyright (C) 2009-2026 Free Software Foundation, Inc. | |
| This program is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| #include <config.h> | |
| #include "idpriv.h" | |
| #include <stdlib.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| int | |
| idpriv_drop (void) | |
| { | |
| #if HAVE_GETUID | |
| uid_t uid = getuid (); | |
| #endif | |
| #if HAVE_GETGID | |
| gid_t gid = getgid (); | |
| #endif | |
| /* Drop the gid privilege first, because in some cases the gid privilege | |
| cannot be dropped after the uid privilege has been dropped. */ | |
| /* This is for executables that have the setgid bit set. */ | |
| #if HAVE_SETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */ | |
| /* This code is needed: In particular, on HP-UX 11.11, setregid (gid, gid) | |
| may leave the saved gid as 0. See also the comment below regarding | |
| setresuid. */ | |
| if (setresgid (gid, gid, gid) < 0) | |
| return -1; | |
| #elif HAVE_SETREGID /* Mac OS X, NetBSD, AIX, Solaris, Cygwin */ | |
| if (setregid (gid, gid) < 0) | |
| return -1; | |
| #elif HAVE_SETEGID /* Solaris 2.4 */ | |
| if (setegid (gid) < 0) | |
| return -1; | |
| #endif | |
| /* This is for executables that have the setuid bit set. */ | |
| #if HAVE_SETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */ | |
| /* On systems which have setresuid(), we use it instead of setreuid(), | |
| because | |
| Hao Chen, David Wagner, Drew Dean: Setuid Demystified | |
| <https://www.usenix.org/legacy/publications/library/proceedings/sec02/full_papers/chen/chen.pdf> | |
| says about setreuid(): "The rule by which the saved uid id is modified | |
| is complicated." Similarly, <https://unixpapa.com/incnote/setuid.html> | |
| says about setreuid(): "What exactly happens to the saved UID when this | |
| is used seems to vary a lot." */ | |
| if (setresuid (uid, uid, uid) < 0) | |
| return -1; | |
| #elif HAVE_SETREUID /* Mac OS X, NetBSD, AIX, Solaris, Cygwin */ | |
| if (setreuid (uid, uid) < 0) | |
| return -1; | |
| #elif HAVE_SETEUID /* Solaris 2.4 */ | |
| if (seteuid (uid) < 0) | |
| return -1; | |
| #endif | |
| /* Verify that the privileges have really been dropped. | |
| This verification is here for security reasons. Doesn't matter if it | |
| takes a couple of system calls. | |
| On Solaris (which has saved uids and gids but no getresuid, getresgid | |
| functions), we could read /proc/<pid>/cred and verify the saved uid and | |
| gid found there. But it's not clear to me when to interpret the file as a | |
| 'prcred_t' and when as a 'prcred32_t'. | |
| Hao Chen, David Wagner, Drew Dean: Setuid Demystified | |
| <https://www.usenix.org/legacy/publications/library/proceedings/sec02/full_papers/chen/chen.pdf> | |
| section 8.1.3 also recommends to use a setreuid call as a probe, but | |
| this call would unexpectedly succeed (and the verification thus fail) | |
| on Linux if the process has the CAP_SETUID capability. | |
| When the verification fails, it indicates that we need to use different | |
| API in the code above. Therefore 'abort ()', not 'return -1'. */ | |
| #if HAVE_GETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */ | |
| { | |
| uid_t real; | |
| uid_t effective; | |
| uid_t saved; | |
| if (getresuid (&real, &effective, &saved) < 0 | |
| || real != uid | |
| || effective != uid | |
| || saved != uid) | |
| abort (); | |
| } | |
| #else | |
| # if HAVE_GETEUID | |
| if (geteuid () != uid) | |
| abort (); | |
| # endif | |
| # if HAVE_GETUID | |
| if (getuid () != uid) | |
| abort (); | |
| # endif | |
| #endif | |
| #if HAVE_GETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */ | |
| { | |
| gid_t real; | |
| gid_t effective; | |
| gid_t saved; | |
| if (getresgid (&real, &effective, &saved) < 0 | |
| || real != gid | |
| || effective != gid | |
| || saved != gid) | |
| abort (); | |
| } | |
| #else | |
| # if HAVE_GETEGID | |
| if (getegid () != gid) | |
| abort (); | |
| # endif | |
| # if HAVE_GETGID | |
| if (getgid () != gid) | |
| abort (); | |
| # endif | |
| #endif | |
| return 0; | |
| } |