maint: update copyright
[gnulib.git] / lib / idpriv-droptemp.c
1 /* Dropping uid/gid privileges of the current process temporarily.
2    Copyright (C) 2009-2014 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 <http://www.gnu.org/licenses/>.  */
16
17 #include <config.h>
18
19 #include "idpriv.h"
20
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 /* The privileged uid and gid that the process had earlier.  */
27 #if HAVE_GETUID
28 static int saved_uid = -1;
29 #endif
30 #if HAVE_GETGID
31 static int saved_gid = -1;
32 #endif
33
34 int
35 idpriv_temp_drop (void)
36 {
37 #if HAVE_GETEUID && HAVE_GETEGID && (HAVE_SETRESUID || HAVE_SETREUID) && (HAVE_SETRESGID || HAVE_SETREGID)
38   int uid = getuid ();
39   int gid = getgid ();
40
41   /* Find out about the privileged uid and gid at the first call.  */
42   if (saved_uid == -1)
43     saved_uid = geteuid ();
44   if (saved_gid == -1)
45     saved_gid = getegid ();
46
47   /* Drop the gid privilege first, because in some cases the gid privilege
48      cannot be dropped after the uid privilege has been dropped.  */
49
50   /* This is for executables that have the setgid bit set.  */
51 # if HAVE_SETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */
52   if (setresgid (-1, gid, saved_gid) < 0)
53     return -1;
54 # else /* Mac OS X, NetBSD, AIX, IRIX, Solaris >= 2.5, OSF/1, Cygwin */
55   if (setregid (-1, gid) < 0)
56     return -1;
57 # endif
58
59   /* This is for executables that have the setuid bit set.  */
60 # if HAVE_SETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */
61   /* See <http://www.usenix.org/events/sec02/full_papers/chen/chen.pdf>
62      figure 14.  */
63   if (setresuid (-1, uid, saved_uid) < 0)
64     return -1;
65 # else /* Mac OS X, NetBSD, AIX, IRIX, Solaris >= 2.5, OSF/1, Cygwin */
66   if (setreuid (-1, uid) < 0)
67     return -1;
68 # endif
69
70   /* Verify that the privileges have really been dropped.
71      This verification is here for security reasons.  Doesn't matter if it
72      takes a couple of system calls.
73      When the verification fails, it indicates that we need to use different
74      API in the code above. Therefore 'abort ()', not 'return -1'.  */
75 # if HAVE_GETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */
76   {
77     uid_t real;
78     uid_t effective;
79     uid_t saved;
80     if (getresuid (&real, &effective, &saved) < 0
81         || real != uid
82         || effective != uid
83         || saved != saved_uid)
84       abort ();
85   }
86 # else
87 #  if HAVE_GETEUID
88   if (geteuid () != uid)
89     abort ();
90 #  endif
91   if (getuid () != uid)
92     abort ();
93 # endif
94 # if HAVE_GETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */
95   {
96     uid_t real;
97     uid_t effective;
98     uid_t saved;
99     if (getresgid (&real, &effective, &saved) < 0
100         || real != gid
101         || effective != gid
102         || saved != saved_gid)
103       abort ();
104   }
105 # else
106 #  if HAVE_GETEGID
107   if (getegid () != gid)
108     abort ();
109 #  endif
110   if (getgid () != gid)
111     abort ();
112 # endif
113
114   return 0;
115 #else
116   errno = ENOSYS;
117   return -1;
118 #endif
119 }
120
121 int
122 idpriv_temp_restore (void)
123 {
124 #if HAVE_GETEUID && HAVE_GETEGID && (HAVE_SETRESUID || HAVE_SETREUID) && (HAVE_SETRESGID || HAVE_SETREGID)
125   int uid = getuid ();
126   int gid = getgid ();
127
128   if (saved_uid == -1 || saved_gid == -1)
129     /* Caller error: idpriv_temp_drop was never invoked.  */
130     abort ();
131
132   /* Acquire the gid privilege last, because in some cases the gid privilege
133      cannot be acquired before the uid privilege has been acquired.  */
134
135   /* This is for executables that have the setuid bit set.  */
136 # if HAVE_SETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */
137   /* See <http://www.usenix.org/events/sec02/full_papers/chen/chen.pdf>
138      figure 14.  */
139   if (setresuid (-1, saved_uid, -1) < 0)
140     return -1;
141 # else /* Mac OS X, NetBSD, AIX, IRIX, Solaris >= 2.5, OSF/1, Cygwin */
142   if (setreuid (-1, saved_uid) < 0)
143     return -1;
144 # endif
145
146   /* This is for executables that have the setgid bit set.  */
147 # if HAVE_SETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */
148   if (setresgid (-1, saved_gid, -1) < 0)
149     return -1;
150 # else /* Mac OS X, NetBSD, AIX, IRIX, Solaris >= 2.5, OSF/1, Cygwin */
151   if (setregid (-1, saved_gid) < 0)
152     return -1;
153 # endif
154
155   /* Verify that the privileges have really been acquired.
156      This verification is here for security reasons.  Doesn't matter if it
157      takes a couple of system calls.
158      When the verification fails, it indicates that we need to use different
159      API in the code above. Therefore 'abort ()', not 'return -1'.  */
160 # if HAVE_GETRESUID /* glibc, FreeBSD, OpenBSD, HP-UX */
161   {
162     uid_t real;
163     uid_t effective;
164     uid_t saved;
165     if (getresuid (&real, &effective, &saved) < 0
166         || real != uid
167         || effective != saved_uid
168         || saved != saved_uid)
169       abort ();
170   }
171 # else
172 #  if HAVE_GETEUID
173   if (geteuid () != saved_uid)
174     abort ();
175 #  endif
176   if (getuid () != uid)
177     abort ();
178 # endif
179 # if HAVE_GETRESGID /* glibc, FreeBSD, OpenBSD, HP-UX */
180   {
181     uid_t real;
182     uid_t effective;
183     uid_t saved;
184     if (getresgid (&real, &effective, &saved) < 0
185         || real != gid
186         || effective != saved_gid
187         || saved != saved_gid)
188       abort ();
189   }
190 # else
191 #  if HAVE_GETEGID
192   if (getegid () != saved_gid)
193     abort ();
194 #  endif
195   if (getgid () != gid)
196     abort ();
197 # endif
198
199   return 0;
200 #else
201   errno = ENOSYS;
202   return -1;
203 #endif
204 }