1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
/* 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 <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, 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
<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, 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/<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;
}
|