1 /* @(#)setuid.c	1.23 15/09/15 Copyright 1998,1999,2004 Heiko Eissfeldt, Copyright 2004-2011 J. Schilling */
2 #include "config.h"
3 #ifndef lint
4 static	UConst char sccsid[] =
5 "@(#)setuid.c	1.23 15/09/15 Copyright 1998,1999,2004 Heiko Eissfeldt, Copyright 2004-2011 J. Schilling";
6 
7 #endif
8 /*
9  * Security functions
10  *
11  * If these functions fail, it is because there was an installation error
12  * or a programming error, and we can't be sure about what privileges
13  * we do or do not have.  This means we might not be able to recover
14  * the privileges we need to fix anything that may be broken (e.g. the
15  * CDDA state of some interface types), and we may in fact do something
16  * quite dangerous (like write to the WAV file as root).
17  *
18  * In any case, it is unsafe to do anything but exit *now*.  Ideally we'd
19  * kill -9 our process group too, just to be sure.  Root privileges are not
20  * something you want floating around at random in user-level applications.
21  *
22  * If any signal handlers or child processes are introduced into this
23  * program, it will be necessary to call dontneedroot() or neverneedroot()
24  * on entry, respectively; otherwise, it will be possible to trick
25  * the program into executing the signal handler or child process with
26  * root privileges by sending signals at the right time.
27  */
28 /*
29  * The contents of this file are subject to the terms of the
30  * Common Development and Distribution License, Version 1.0 only
31  * (the "License").  You may not use this file except in compliance
32  * with the License.
33  *
34  * See the file CDDL.Schily.txt in this distribution for details.
35  * A copy of the CDDL is also available via the Internet at
36  * http://www.opensource.org/licenses/cddl1.txt
37  *
38  * When distributing Covered Code, include this CDDL HEADER in each
39  * file and include the License file CDDL.Schily.txt from this distribution.
40  */
41 
42 #include "config.h"
43 #include <schily/unistd.h>
44 #include <schily/stdio.h>
45 #include <schily/stdlib.h>
46 #include <schily/schily.h>
47 #include <schily/nlsdefs.h>
48 
49 #include "exitcodes.h"
50 #include "setuid.h"
51 
52 /*#undef DEBUG*/
53 /*#define DEBUG*/
54 
55 /* True at return from initsecurity */
56 static uid_t real_uid = (uid_t) (-1);
57 static uid_t effective_uid = (uid_t) (-1);
58 static gid_t real_gid = (gid_t) (-1);
59 static gid_t effective_gid = (gid_t) (-1);
60 
61 /*
62  * Run this at the beginning of the program to initialize this code and
63  * to drop privileges before someone uses them to shoot us in the foot.
64  * Do not pass(go), do not dollars += 200.
65  */
66 void
initsecurity()67 initsecurity()
68 {
69 	int	leffective_uid;
70 
71 #ifdef	HAVE_ALARM
72 	alarm(0);		/* can be inherited from parent process */
73 #endif
74 	real_uid = getuid();
75 	leffective_uid = geteuid();
76 	if ((int) real_uid != leffective_uid && leffective_uid != 0) { /* sanity check */
77 		errmsgno(EX_BAD,
78 		_("Warning: setuid but not to root (uid=%ld, euid=%d)\n"),
79 				(long) real_uid, leffective_uid);
80 		fprintf(stderr, _("Dropping setuid privileges now.\n"));
81 		neverneedroot();
82 	} else {
83 		effective_uid = leffective_uid;
84 	}
85 	real_gid = getgid();
86 	effective_gid = getegid();
87 	dontneedroot();
88 	dontneedgroup();
89 }
90 
91 /* Temporarily gain root privileges. */
92 
93 #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEUID) && defined SCO
94 /* SCO seems to lack the prototypes... */
95 int	seteuid __PR((uid_t euid));
96 int	setegid __PR((gid_t guid));
97 #endif
98 
99 void
needroot(necessary)100 needroot(necessary)
101 	int	necessary;
102 {
103 #ifdef DEBUG
104 	fprintf(stderr,
105 	"call to     needroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n",
106 			effective_uid, real_uid,
107 			geteuid(), getuid(), getpid());
108 #endif
109 	if (effective_uid) {
110 		if (necessary) {
111 			errmsgno(EX_BAD,
112 			_("Fatal error: require root privilege but not setuid root.\n"));
113 			exit(PERM_ERROR);
114 		} else
115 			return;
116 	}
117 	if (real_uid == (uid_t) (-1)) {
118 		errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n"));
119 		exit(INTERNAL_ERROR);
120 	}
121 
122 	if (geteuid() == 0)
123 		return; /* nothing to do */
124 
125 #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEUID)
126 	if (seteuid(effective_uid)) {
127 		errmsg(_("Error with seteuid in needroot().\n"));
128 		exit(PERM_ERROR);
129 	}
130 #else
131 #if defined(HAVE_SETREUID)
132 	if (setreuid(real_uid, effective_uid)) {
133 		errmsg(_("Error with setreuid in needroot().\n"));
134 		exit(PERM_ERROR);
135 	}
136 #endif
137 #endif
138 	    if (geteuid() != 0 && necessary) {
139 		errmsgno(EX_BAD, _("Fatal error: did not get root privilege.\n"));
140 		exit(PERM_ERROR);
141 	}
142 #ifdef DEBUG
143 	fprintf(stderr,
144 	"exit of     needroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n",
145 			effective_uid, real_uid,
146 			geteuid(), getuid(), getpid());
147 #endif
148 }
149 
150 /*
151  * Temporarily drop root privileges.
152  */
153 void
dontneedroot()154 dontneedroot()
155 {
156 #ifdef DEBUG
157 	fprintf(stderr,
158 	"call to dontneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n",
159 			effective_uid, real_uid,
160 			geteuid(), getuid(), getpid());
161 #endif
162 	if (real_uid == (uid_t) (-1)) {
163 		errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n"));
164 		exit(INTERNAL_ERROR);
165 	}
166 	if (effective_uid)
167 		return;
168 	if (geteuid() != 0)
169 		return; /* nothing to do */
170 
171 #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEUID)
172 	if (seteuid(real_uid)) {
173 		errmsg(_("Error with seteuid in dontneedroot().\n"));
174 		exit(PERM_ERROR);
175 	}
176 #else
177 #if defined(HAVE_SETREUID)
178 	if (setreuid(effective_uid, real_uid)) {
179 		errmsg(_("Error with setreuid in dontneedroot().\n"));
180 		exit(PERM_ERROR);
181 	}
182 #endif
183 #endif
184 	if (geteuid() != real_uid) {
185 		errmsgno(EX_BAD,
186 			_("Fatal error: did not drop root privilege.\n"));
187 #ifdef DEBUG
188 		fprintf(stderr,
189 		"in   to dontneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n",
190 			effective_uid, real_uid,
191 			geteuid(), getuid(), getpid());
192 #endif
193 		exit(PERM_ERROR);
194 	}
195 }
196 
197 /*
198  * Permanently drop root privileges.
199  */
200 void
neverneedroot()201 neverneedroot()
202 {
203 #ifdef DEBUG
204 	fprintf(stderr,
205 	"call to neverneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n",
206 			effective_uid, real_uid,
207 			geteuid(), getuid(), getpid());
208 #endif
209 	if (real_uid == (uid_t) (-1)) {
210 		errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n"));
211 		exit(INTERNAL_ERROR);
212 	}
213 	if (geteuid() != effective_uid) {
214 		needroot(1);
215 	}
216 	if (geteuid() == effective_uid) {
217 #if defined(HAVE_SETUID)
218 		if (setuid(real_uid)) {
219 			errmsg(_("Error with setuid in neverneedroot().\n"));
220 			exit(PERM_ERROR);
221 		}
222 #endif
223 	}
224 #if	defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
225 	defined(__DragonFly__)	/* XXX this is a big hack and and not a permanent solution */
226 	else {
227 #if defined(HAVE_SETUID)
228 		if (setuid(real_uid)) {
229 			errmsg(_("Error with setuid in neverneedroot().\n"));
230 			exit(PERM_ERROR);
231 		}
232 #endif
233 	}
234 #endif
235 	if (geteuid() != real_uid || getuid() != real_uid) {
236 		errmsgno(EX_BAD,
237 		_("Fatal error: did not drop root privilege.\n"));
238 #ifdef DEBUG
239 		fprintf(stderr,
240 		"in  to neverneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n",
241 			effective_uid, real_uid,
242 			geteuid(), getuid(), getpid());
243 #endif
244 		exit(PERM_ERROR);
245 	}
246 	effective_uid = real_uid;
247 #ifdef DEBUG
248 	fprintf(stderr,
249 	"exit of neverneedroot (_euid_=%d, uid=%d), current=%d/%d, pid=%d\n",
250 			effective_uid, real_uid,
251 			geteuid(), getuid(), getpid());
252 #endif
253 }
254 
255 /* Temporarily gain group privileges. */
256 
257 void
needgroup(necessary)258 needgroup(necessary)
259 	int	necessary;
260 {
261 #ifdef DEBUG
262 	fprintf(stderr,
263 	"call to     needgroup (egid=%d, gid=%d), current=%d/%d, pid=%d\n",
264 			effective_gid, real_gid,
265 			getegid(), getgid(), getpid());
266 #endif
267 	if (real_gid == (gid_t) (-1)) {
268 		errmsgno(EX_BAD,
269 		_("Fatal error: initsecurity() not called.\n"));
270 		exit(INTERNAL_ERROR);
271 	}
272 
273 	if (getegid() == effective_gid)
274 		return; /* nothing to do */
275 
276 #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEGID)
277 	if (setegid(effective_gid)) {
278 		errmsg(_("Error with setegid in needgroup().\n"));
279 		exit(PERM_ERROR);
280 	}
281 #else
282 #if defined(HAVE_SETREGID)
283 	if (setregid(real_gid, effective_gid)) {
284 		errmsg(_("Error with setregid in needgroup().\n"));
285 		exit(PERM_ERROR);
286 	}
287 #endif
288 #endif
289 	if (necessary && getegid() != effective_gid) {
290 		errmsgno(EX_BAD,
291 			_("Fatal error: did not get group privilege.\n"));
292 		exit(PERM_ERROR);
293 	}
294 }
295 
296 /*
297  * Temporarily drop group privileges.
298  */
299 void
dontneedgroup()300 dontneedgroup()
301 {
302 #ifdef DEBUG
303 	fprintf(stderr,
304 	"call to dontneedgroup (egid=%d, gid=%d), current=%d/%d, pid=%d\n",
305 			effective_gid, real_gid,
306 			getegid(), getgid(), getpid());
307 #endif
308 	if (real_gid == (gid_t) (-1)) {
309 		errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n"));
310 		exit(INTERNAL_ERROR);
311 	}
312 	if (getegid() != effective_gid)
313 		return; /* nothing to do */
314 #if defined _POSIX_SAVED_IDS && defined(HAVE_SETEGID)
315 	if (setegid(real_gid)) {
316 		errmsg(_("Error with setegid in dontneedgroup().\n"));
317 		exit(PERM_ERROR);
318 	}
319 #else
320 #if defined(HAVE_SETREGID)
321 	if (setregid(effective_gid, real_gid)) {
322 		errmsg(_("Error with setregid in dontneedgroup().\n"));
323 		exit(PERM_ERROR);
324 	}
325 #endif
326 #endif
327 	if (getegid() != real_gid) {
328 		errmsgno(EX_BAD,
329 			_("Fatal error: did not drop group privilege.\n"));
330 		exit(PERM_ERROR);
331 	}
332 #ifdef DEBUG
333 	fprintf(stderr,
334 	"exit if dontneedgroup (egid=%d, gid=%d), current=%d/%d, pid=%d\n",
335 			effective_gid, real_gid,
336 			getegid(), getgid(), getpid());
337 #endif
338 }
339 
340 /*
341  * Permanently drop group privileges.
342  */
343 void
neverneedgroup()344 neverneedgroup()
345 {
346 #ifdef DEBUG
347 	fprintf(stderr,
348 	"call to neverneedgroup (egid=%d, gid=%d), current=%d/%d, pid=%d\n",
349 			effective_gid, real_gid,
350 			getegid(), getgid(), getpid());
351 #endif
352 	if (real_gid == (gid_t) (-1)) {
353 		errmsgno(EX_BAD, _("Fatal error: initsecurity() not called.\n"));
354 		exit(INTERNAL_ERROR);
355 	}
356 	if (getegid() != effective_gid) {
357 		needgroup(1);
358 	}
359 	if (getegid() == effective_gid) {
360 #if defined(HAVE_SETGID)
361 		if (setgid(real_gid)) {
362 			errmsg(_("Error with setgid in neverneedgroup().\n"));
363 			exit(PERM_ERROR);
364 		}
365 #endif
366 	}
367 #if	defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
368 	defined(__DragonFly__)	/* XXX this is a big hack and and not a permanent solution */
369 	else {
370 #if defined(HAVE_SETGID)
371 		if (setgid(real_gid)) {
372 			errmsg(_("Error with setgid in neverneedgroup().\n"));
373 			exit(PERM_ERROR);
374 		}
375 #endif
376 	}
377 #endif
378 	if (getegid() != real_gid || getgid() != real_gid) {
379 		errmsgno(EX_BAD,
380 			_("Fatal error: did not drop group privilege.\n"));
381 #ifdef DEBUG
382 		fprintf(stderr,
383 		"in  to neverneedgroup (_egid_=%d, gid=%d), current=%d/%d, pid=%d\n",
384 			effective_gid, real_gid,
385 			getegid(), getgid(), getpid());
386 #endif
387 		exit(PERM_ERROR);
388 	}
389 	effective_gid = real_gid;
390 }
391 
392 #if defined(HPUX)
393 int
seteuid(uid)394 seteuid(uid)
395 	uid_t	uid;
396 {
397 	return (setresuid(-1, uid, -1));
398 }
399 
400 int
setreuid(uid1,uid2)401 setreuid(uid1, uid2)
402 	uid_t	uid1;
403 	uid_t	uid2;
404 {
405 	return (setresuid(uid2, uid2, uid1 == uid2 ? uid2 : 0));
406 }
407 
408 int
setregid(gid1,gid2)409 setregid(gid1, gid2)
410 	gid_t	gid1;
411 	gid_t	gid2;
412 {
413 	return (setresgid(gid2, gid2, gid1 == gid2 ? gid2 : 0));
414 }
415 #endif
416