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