/* Dropping uid/gid privileges of the current process permanently. Copyright (C) 2009-2024 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 . */ #include #include "idpriv.h" #include #include #include int idpriv_drop (void) { #if HAVE_GETUID int uid = getuid (); #endif #if HAVE_GETGID int 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, IRIX, Solaris, OSF/1, 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 says about setreuid(): "The rule by which the saved uid id is modified is complicated." Similarly, 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, IRIX, Solaris, OSF/1, 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//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 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; }