1/*
2   Unix SMB/Netbios implementation.
3   Version 2.0
4   Copyright (C) Jeremy Allison 1998.
5   rewritten for version 2.0.6 by Tridge
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*/
21
22#ifndef AUTOCONF_TEST
23#include "includes.h"
24extern int DEBUGLEVEL;
25#else
26/* we are running this code in autoconf test mode to see which type of setuid
27   function works */
28#if defined(HAVE_UNISTD_H)
29#include <unistd.h>
30#endif
31#include <stdlib.h>
32#include <stdio.h>
33#include <sys/types.h>
34#include <errno.h>
35
36#ifdef HAVE_SYS_PRIV_H
37#include <sys/priv.h>
38#endif
39#ifdef HAVE_SYS_ID_H
40#include <sys/id.h>
41#endif
42
43#define DEBUG(x, y) printf y
44#define smb_panic(x) exit(1)
45#endif
46
47/****************************************************************************
48abort if we haven't set the uid correctly
49****************************************************************************/
50static void assert_uid(uid_t ruid, uid_t euid)
51{
52	if ((euid != (uid_t)-1 && geteuid() != euid) ||
53	    (ruid != (uid_t)-1 && getuid() != ruid)) {
54		DEBUG(0,("Failed to set uid privileges to (%d,%d) now set to (%d,%d)\n",
55			 (int)ruid, (int)euid,
56			 (int)getuid(), (int)geteuid()));
57		smb_panic("failed to set uid\n");
58		exit(1);
59	}
60}
61
62/****************************************************************************
63abort if we haven't set the gid correctly
64****************************************************************************/
65static void assert_gid(gid_t rgid, gid_t egid)
66{
67	if ((egid != (gid_t)-1 && getegid() != egid) ||
68	    (rgid != (gid_t)-1 && getgid() != rgid)) {
69		DEBUG(0,("Failed to set gid privileges to (%d,%d) now set to (%d,%d) uid=(%d,%d)\n",
70			 (int)rgid, (int)egid,
71			 (int)getgid(), (int)getegid(),
72			 (int)getuid(), (int)geteuid()));
73		smb_panic("failed to set gid\n");
74		exit(1);
75	}
76}
77
78/****************************************************************************
79 Gain root privilege before doing something.
80 We want to end up with ruid==euid==0
81****************************************************************************/
82void gain_root_privilege(void)
83{
84#if USE_SETRESUID
85	setresuid(0,0,0);
86#endif
87
88#if USE_SETEUID
89	seteuid(0);
90#endif
91
92#if USE_SETREUID
93	setreuid(0, 0);
94#endif
95
96#if USE_SETUIDX
97	setuidx(ID_EFFECTIVE, 0);
98	setuidx(ID_REAL, 0);
99#endif
100
101	/* this is needed on some systems */
102	setuid(0);
103
104	assert_uid(0, 0);
105}
106
107
108/****************************************************************************
109 Ensure our real and effective groups are zero.
110 we want to end up with rgid==egid==0
111****************************************************************************/
112void gain_root_group_privilege(void)
113{
114#if USE_SETRESUID
115	setresgid(0,0,0);
116#endif
117
118#if USE_SETREUID
119	setregid(0,0);
120#endif
121
122#if USE_SETEUID
123	setegid(0);
124#endif
125
126#if USE_SETUIDX
127	setgidx(ID_EFFECTIVE, 0);
128	setgidx(ID_REAL, 0);
129#endif
130
131	setgid(0);
132
133	assert_gid(0, 0);
134}
135
136
137/****************************************************************************
138 Set *only* the effective uid.
139 we want to end up with ruid==0 and euid==uid
140****************************************************************************/
141void set_effective_uid(uid_t uid)
142{
143#if USE_SETRESUID
144	setresuid(-1,uid,-1);
145#endif
146
147#if USE_SETREUID
148	setreuid(-1,uid);
149#endif
150
151#if USE_SETEUID
152	seteuid(uid);
153#endif
154
155#if USE_SETUIDX
156	setuidx(ID_EFFECTIVE, uid);
157#endif
158
159	assert_uid(-1, uid);
160}
161
162/****************************************************************************
163 Set *only* the effective gid.
164 we want to end up with rgid==0 and egid==gid
165****************************************************************************/
166void set_effective_gid(gid_t gid)
167{
168#if USE_SETRESUID
169	setresgid(-1,gid,-1);
170#endif
171
172#if USE_SETREUID
173	setregid(-1,gid);
174#endif
175
176#if USE_SETEUID
177	setegid(gid);
178#endif
179
180#if USE_SETUIDX
181	setgidx(ID_EFFECTIVE, gid);
182#endif
183
184	assert_gid(-1, gid);
185}
186
187static uid_t saved_euid, saved_ruid;
188
189/****************************************************************************
190 save the real and effective uid for later restoration. Used by the quotas
191 code
192****************************************************************************/
193void save_re_uid(void)
194{
195	saved_ruid = getuid();
196	saved_euid = geteuid();
197}
198
199
200/****************************************************************************
201 and restore them!
202****************************************************************************/
203void restore_re_uid(void)
204{
205	set_effective_uid(0);
206
207#if USE_SETRESUID
208	setresuid(saved_ruid, saved_euid, -1);
209#elif USE_SETREUID
210	setreuid(saved_ruid, -1);
211	setreuid(-1,saved_euid);
212#elif USE_SETUIDX
213	setuidx(ID_REAL, saved_ruid);
214	setuidx(ID_EFFECTIVE, saved_euid);
215#else
216	set_effective_uid(saved_euid);
217	if (getuid() != saved_ruid)
218		setuid(saved_ruid);
219	set_effective_uid(saved_euid);
220#endif
221
222	assert_uid(saved_ruid, saved_euid);
223}
224
225/****************************************************************************
226 set the real AND effective uid to the current effective uid in a way that
227 allows root to be regained.
228 This is only possible on some platforms.
229****************************************************************************/
230int set_re_uid(void)
231{
232	uid_t uid = geteuid();
233
234#if USE_SETRESUID
235	setresuid(geteuid(), -1, -1);
236#endif
237
238#if USE_SETREUID
239	setreuid(0, 0);
240	setreuid(uid, -1);
241	setreuid(-1, uid);
242#endif
243
244#if USE_SETEUID
245	/* can't be done */
246	return -1;
247#endif
248
249#if USE_SETUIDX
250	/* can't be done */
251	return -1;
252#endif
253
254	assert_uid(uid, uid);
255	return 0;
256}
257
258
259/****************************************************************************
260 Become the specified uid and gid - permanently !
261 there should be no way back if possible
262****************************************************************************/
263void become_user_permanently(uid_t uid, gid_t gid)
264{
265	/*
266	 * First - gain root privilege. We do this to ensure
267	 * we can lose it again.
268	 */
269
270	gain_root_privilege();
271	gain_root_group_privilege();
272
273#if USE_SETRESUID
274	setresgid(gid,gid,gid);
275	setgid(gid);
276	setresuid(uid,uid,uid);
277	setuid(uid);
278#endif
279
280#if USE_SETREUID
281	setregid(gid,gid);
282	setgid(gid);
283	setreuid(uid,uid);
284	setuid(uid);
285#endif
286
287#if USE_SETEUID
288	setegid(gid);
289	setgid(gid);
290	setuid(uid);
291	seteuid(uid);
292	setuid(uid);
293#endif
294
295#if USE_SETUIDX
296	setgidx(ID_REAL, gid);
297	setgidx(ID_EFFECTIVE, gid);
298	setgid(gid);
299	setuidx(ID_REAL, uid);
300	setuidx(ID_EFFECTIVE, uid);
301	setuid(uid);
302#endif
303
304	assert_uid(uid, uid);
305	assert_gid(gid, gid);
306}
307
308
309/****************************************************************************
310this function just checks that we don't get ENOSYS back
311****************************************************************************/
312static int have_syscall(void)
313{
314	errno = 0;
315
316#if USE_SETRESUID
317	setresuid(-1,-1,-1);
318#endif
319
320#if USE_SETREUID
321	setreuid(-1,-1);
322#endif
323
324#if USE_SETEUID
325	seteuid(-1);
326#endif
327
328#if USE_SETUIDX
329	setuidx(ID_EFFECTIVE, -1);
330#endif
331
332	if (errno == ENOSYS) return -1;
333
334	return 0;
335}
336
337#ifdef AUTOCONF_TEST
338main()
339{
340        if (getuid() != 0) {
341#if (defined(AIX) && defined(USE_SETREUID))
342		/* setreuid is badly broken on AIX 4.1, we avoid it completely */
343                fprintf(stderr,"avoiding possibly broken setreuid\n");
344		exit(1);
345#endif
346
347		/* if not running as root then at least check to see if we get ENOSYS - this
348		   handles Linux 2.0.x with glibc 2.1 */
349                fprintf(stderr,"not running as root: checking for ENOSYS\n");
350		exit(have_syscall());
351	}
352
353	gain_root_privilege();
354	gain_root_group_privilege();
355	set_effective_gid(1);
356	set_effective_uid(1);
357	save_re_uid();
358	restore_re_uid();
359	gain_root_privilege();
360	gain_root_group_privilege();
361	become_user_permanently(1, 1);
362	setuid(0);
363	if (getuid() == 0) {
364		fprintf(stderr,"uid not set permanently\n");
365		exit(1);
366	}
367
368	printf("OK\n");
369
370	exit(0);
371}
372#endif
373