1 /* Dropping uid/gid privileges of the current process permanently. 2 Copyright (C) 2009-2021 Free Software Foundation, Inc. 3 4 This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 3 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 16 17 #include <config.h> 18 19 #include "idpriv.h" 20 21 #include <stdlib.h> 22 #include <sys/types.h> 23 #include <unistd.h> 24 25 int 26 idpriv_drop (void) /* */ 27 { 28 #if HAVE_GETUID 29 int uid = getuid (); 30 #endif 31 #if HAVE_GETGID 32 int gid = getgid (); 33 #endif 34 35 /* Drop the gid privilege first, because in some cases the gid privilege 36 cannot be dropped after the uid privilege has been dropped. */ 37 38 /* This is for executables that have the setgid bit set. */ 39 #if HAVE_SETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */ 40 /* This code is needed: In particular, on HP-UX 11.11, setregid (gid, gid) 41 may leave the saved gid as 0. See also the comment below regarding 42 setresuid. */ 43 if (setresgid (gid, gid, gid) < 0) 44 return -1; 45 #elif HAVE_SETREGID /* Mac OS X, NetBSD, AIX, IRIX, Solaris, OSF/1, Cygwin */ 46 if (setregid (gid, gid) < 0) 47 return -1; 48 #elif HAVE_SETEGID /* Solaris 2.4 */ 49 if (setegid (gid) < 0) 50 return -1; 51 #endif 52 53 /* This is for executables that have the setuid bit set. */ 54 #if HAVE_SETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */ 55 /* On systems which have setresuid(), we use it instead of setreuid(), 56 because 57 Hao Chen, David Wagner, Drew Dean: Setuid Demystified 58 <https://www.usenix.org/legacy/publications/library/proceedings/sec02/full_papers/chen/chen.pdf> 59 says about setreuid(): "The rule by which the saved uid id is modified 60 is complicated." Similarly, <https://unixpapa.com/incnote/setuid.html> 61 says about setreuid(): "What exactly happens to the saved UID when this 62 is used seems to vary a lot." */ 63 if (setresuid (uid, uid, uid) < 0) 64 return -1; 65 #elif HAVE_SETREUID /* Mac OS X, NetBSD, AIX, IRIX, Solaris, OSF/1, Cygwin */ 66 if (setreuid (uid, uid) < 0) 67 return -1; 68 #elif HAVE_SETEUID /* Solaris 2.4 */ 69 if (seteuid (uid) < 0) 70 return -1; 71 #endif 72 73 /* Verify that the privileges have really been dropped. 74 This verification is here for security reasons. Doesn't matter if it 75 takes a couple of system calls. 76 On Solaris (which has saved uids and gids but no getresuid, getresgid 77 functions), we could read /proc/<pid>/cred and verify the saved uid and 78 gid found there. But it's not clear to me when to interpret the file as a 79 'prcred_t' and when as a 'prcred32_t'. 80 Hao Chen, David Wagner, Drew Dean: Setuid Demystified 81 <https://www.usenix.org/legacy/publications/library/proceedings/sec02/full_papers/chen/chen.pdf> 82 section 8.1.3 also recommends to use a setreuid call as a probe, but 83 this call would unexpectedly succeed (and the verification thus fail) 84 on Linux if the process has the CAP_SETUID capability. 85 When the verification fails, it indicates that we need to use different 86 API in the code above. Therefore 'abort ()', not 'return -1'. */ 87 #if HAVE_GETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */ 88 { 89 uid_t real; 90 uid_t effective; 91 uid_t saved; 92 if (getresuid (&real, &effective, &saved) < 0 93 || real != uid 94 || effective != uid 95 || saved != uid) 96 abort (); 97 } 98 #else 99 # if HAVE_GETEUID 100 if (geteuid () != uid) 101 abort (); 102 # endif 103 # if HAVE_GETUID 104 if (getuid () != uid) 105 abort (); 106 # endif 107 #endif 108 #if HAVE_GETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */ 109 { 110 gid_t real; 111 gid_t effective; 112 gid_t saved; 113 if (getresgid (&real, &effective, &saved) < 0 114 || real != gid 115 || effective != gid 116 || saved != gid) 117 abort (); 118 } 119 #else 120 # if HAVE_GETEGID 121 if (getegid () != gid) 122 abort (); 123 # endif 124 # if HAVE_GETGID 125 if (getgid () != gid) 126 abort (); 127 # endif 128 #endif 129 130 return 0; 131 }