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
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
<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;
}
|