1 /* @(#)priv.c	1.4 13/10/12 Copyright 2006-2013 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)priv.c	1.4 13/10/12 Copyright 2006-2013 J. Schilling";
6 #endif
7 /*
8  *	Cdrecord support functions to support fine grained privileges.
9  *
10  *	Copyright (c) 2006-2013 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/mconfig.h>
27 #include <schily/standard.h>
28 #include <schily/stdlib.h>
29 #include <schily/string.h>
30 #include <schily/schily.h>
31 #include <schily/priv.h>
32 
33 #ifdef	HAVE_LINUX_CAPS
34 #define	NCAPS		(sizeof (caplist) / sizeof (caplist[0]))
35 #endif
36 
37 #ifdef	CDDA2WAV
38 EXPORT	void	priv_init	__PR((void));
39 EXPORT	void	priv_on		__PR((void));
40 EXPORT	void	priv_off	__PR((void));
41 #endif
42 #if	defined(CDRECORD) || defined(READCD)
43 EXPORT	void	priv_drop	__PR((void));
44 EXPORT	BOOL	priv_from_priv	__PR((void));
45 #endif
46 EXPORT	BOOL	priv_eff_priv	__PR((int pname));
47 #ifdef	HAVE_SOLARIS_PPRIV
48 LOCAL	BOOL	priv_eff	__PR((const char *pname));
49 #endif
50 #ifdef	HAVE_LINUX_CAPS
51 LOCAL	BOOL	priv_eff	__PR((int pname));
52 #endif
53 
54 #ifdef	HAVE_SOLARIS_PPRIV
55 EXPORT	void	do_pfexec	__PR((int ac, char *av[], ...));
56 #endif
57 
58 /*
59  * Solaris uses the advanced privileges(5) mechanism for fine grained
60  * prilileges.
61  *
62  * The following Solaris privileges are needed by cdrecord:
63  *
64  *	PRIV_FILE_DAC_READ	only to open /dev/ nodes for USCSICMD
65  *	PRIV_FILE_DAC_WRITE	in addition to open /dev/scg* for SCGIO_CMD
66  *	PRIV_SYS_DEVICES	during whole runtime: for USCSICMD
67  *	PRIV_PROC_LOCK_MEMORY	only to call mlockall()
68  *	PRIV_PROC_PRIOCNTL	only to call priocntl()
69  *	PRIV_NET_PRIVADDR	only to create privileged socket (remote SCSI)
70  *
71  *
72  * The POSIX.1e draft has been withdrawn in 1997.
73  * Linux started to implement this outdated concept in 1997.
74  *
75  * The following Linux capabilities are needed by cdrecord:
76  *
77  *	CAP_DAC_OVERRIDE	only to open /dev/ nodes
78  *	CAP_NET_BIND_SERVICE	only to create privileged socket (remote SCSI)
79  *	CAP_IPC_LOCK		only to call mlockall()
80  *	CAP_SYS_RAWIO		during whole runtime: for SCSI commands
81  *	CAP_SYS_ADMIN		during whole runtime: for SCSI commands
82  *	CAP_SYS_NICE		only to call sched_*() and setpriority()
83  *	CAP_SYS_RESOURCE	only to support mlockall()
84  *
85  * Use:
86  * setcap cap_sys_resource,cap_dac_override,cap_sys_admin,cap_sys_nice,cap_net_bind_service,cap_ipc_lock,cap_sys_rawio+ep cdrecord
87  * to set the capabilities for the executable file.
88  */
89 
90 #ifdef	CDDA2WAV
91 /*
92  * Initial removal of privileges:
93  * -	remove all inheritable additional privileges
94  * -	Remove all effective privileges that are not needed all the time
95  */
96 EXPORT void
priv_init()97 priv_init()
98 {
99 #ifdef	HAVE_PRIV_SET
100 	/*
101 	 * Give up privs we do not need anymore.
102 	 * We no longer need:
103 	 *	file_dac_read,sys_devices,proc_priocntl,net_privaddr
104 	 */
105 	priv_set(PRIV_OFF, PRIV_EFFECTIVE,
106 		PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL,
107 		PRIV_NET_PRIVADDR, NULL);
108 	priv_set(PRIV_OFF, PRIV_INHERITABLE,
109 		PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL,
110 		PRIV_NET_PRIVADDR, PRIV_SYS_DEVICES, NULL);
111 #else
112 #ifdef	HAVE_LINUX_CAPS
113 	/*
114 	 * Give up privs we do not need for the moment.
115 	 * We no longer need:
116 	 *	cap_dac_override,cap_net_bind_service,cap_sys_nice
117 	 */
118 	cap_t		cset;
119 	cap_value_t	caplist[] = {
120 					CAP_DAC_OVERRIDE,
121 					CAP_NET_BIND_SERVICE,
122 					CAP_SYS_NICE,
123 					CAP_SYS_RAWIO,		/* Keep as CAP_EFFECTIVE */
124 					CAP_SYS_ADMIN		/* Keep as CAP_EFFECTIVE */
125 				};
126 
127 	cset = cap_get_proc();
128 
129 	cap_set_flag(cset, CAP_EFFECTIVE, NCAPS-2, caplist, CAP_CLEAR);
130 	cap_set_flag(cset, CAP_INHERITABLE, NCAPS, caplist, CAP_CLEAR);
131 	if (cap_set_proc(cset) < 0)
132 		errmsg("Cannot set initial process capabilities.\n");
133 #endif	/* HAVE_LINUX_CAPS */
134 #endif	/* HAVE_PRIV_SET */
135 }
136 
137 /*
138  * Get back those effective privileges that are needed for parts of the time
139  */
140 EXPORT void
priv_on()141 priv_on()
142 {
143 #ifdef	HAVE_PRIV_SET
144 	/*
145 	 * Get back privs we may need now.
146 	 * We need:
147 	 *	file_dac_read,sys_devices,proc_priocntl,net_privaddr
148 	 */
149 	priv_set(PRIV_ON, PRIV_EFFECTIVE,
150 		PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL,
151 		PRIV_NET_PRIVADDR, NULL);
152 #else
153 #ifdef	HAVE_LINUX_CAPS
154 	/*
155 	 * Get back privs we may need now.
156 	 * We need:
157 	 *	cap_dac_override,cap_net_bind_service,cap_sys_nice
158 	 */
159 	cap_t		cset;
160 	cap_value_t	caplist[] = {
161 					CAP_DAC_OVERRIDE,
162 					CAP_NET_BIND_SERVICE,
163 					CAP_SYS_NICE
164 				};
165 
166 	cset = cap_get_proc();
167 
168 	cap_set_flag(cset, CAP_EFFECTIVE, NCAPS, caplist, CAP_SET);
169 	if (cap_set_proc(cset) < 0)
170 		errmsg("Cannot regain process capabilities.\n");
171 #endif	/* HAVE_LINUX_CAPS */
172 #endif	/* HAVE_PRIV_SET */
173 }
174 
175 /*
176  * Get rid of those effective privileges that are needed for parts of the time
177  */
178 EXPORT void
priv_off()179 priv_off()
180 {
181 #ifdef	HAVE_PRIV_SET
182 	/*
183 	 * Give up privs we do not need at the moment.
184 	 * We no longer need:
185 	 *	file_dac_read,sys_devices,proc_priocntl,net_privaddr
186 	 */
187 	priv_set(PRIV_OFF, PRIV_EFFECTIVE,
188 		PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL,
189 		PRIV_NET_PRIVADDR, NULL);
190 #else
191 #ifdef	HAVE_LINUX_CAPS
192 	/*
193 	 * Give up privs we do not need anymore.
194 	 * We no longer need:
195 	 *	cap_dac_override,cap_net_bind_service,cap_sys_nice
196 	 */
197 	cap_t		cset;
198 	cap_value_t	caplist[] = {
199 					CAP_DAC_OVERRIDE,
200 					CAP_NET_BIND_SERVICE,
201 					CAP_SYS_NICE
202 				};
203 
204 	cset = cap_get_proc();
205 
206 	cap_set_flag(cset, CAP_EFFECTIVE, NCAPS, caplist, CAP_CLEAR);
207 	if (cap_set_proc(cset) < 0)
208 		errmsg("Cannot deactivate process capabilities.\n");
209 #endif	/* HAVE_LINUX_CAPS */
210 #endif	/* HAVE_PRIV_SET */
211 }
212 #endif	/* CDDA2WAV */
213 
214 #if	defined(CDRECORD) || defined(READCD)
215 /*
216  * Drop all privileges that are no longer needed after startup
217  */
218 EXPORT void
priv_drop()219 priv_drop()
220 {
221 #ifdef	HAVE_PRIV_SET
222 #ifdef	PRIVILEGES_DEBUG	/* PRIV_DEBUG is defined in <sys/priv.h> */
223 	error("file_dac_read: %d\n", priv_ineffect(PRIV_FILE_DAC_READ));
224 #endif
225 	/*
226 	 * Give up privs we do not need anymore.
227 	 * We no longer need:
228 	 *	file_dac_read,proc_lock_memory,proc_priocntl,net_privaddr
229 	 * We still need:
230 	 *	sys_devices
231 	 */
232 	priv_set(PRIV_OFF, PRIV_EFFECTIVE,
233 		PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY,
234 		PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, NULL);
235 	priv_set(PRIV_OFF, PRIV_PERMITTED,
236 		PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY,
237 		PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, NULL);
238 	priv_set(PRIV_OFF, PRIV_INHERITABLE,
239 		PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY,
240 		PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, PRIV_SYS_DEVICES, NULL);
241 #else
242 #ifdef	HAVE_LINUX_CAPS
243 	/*
244 	 * Give up privs we do not need anymore.
245 	 * We no longer need:
246 	 *	cap_dac_override,cap_net_bind_service,cap_ipc_lock,cap_sys_nice,cap_sys_resource
247 	 * We still need:
248 	 *	cap_sys_rawio,cap_sys_admin
249 	 */
250 	cap_t		cset;
251 	cap_value_t	caplist[] = {
252 					CAP_DAC_OVERRIDE,
253 					CAP_NET_BIND_SERVICE,
254 					CAP_IPC_LOCK,
255 					CAP_SYS_NICE,
256 					CAP_SYS_RESOURCE
257 				};
258 
259 	cset = cap_get_proc();
260 
261 	cap_set_flag(cset, CAP_EFFECTIVE, NCAPS, caplist, CAP_CLEAR);
262 	cap_set_flag(cset, CAP_INHERITABLE, NCAPS, caplist, CAP_CLEAR);
263 	cap_set_flag(cset, CAP_PERMITTED, NCAPS, caplist, CAP_CLEAR);
264 	if (cap_set_proc(cset) < 0)
265 		errmsg("Cannot drop process capabilities.\n");
266 #endif	/* HAVE_LINUX_CAPS */
267 #endif	/* HAVE_PRIV_SET */
268 }
269 
270 /*
271  * Return TRUE if we have privileges that are not from a suid-root operation.
272  */
273 EXPORT BOOL
priv_from_priv()274 priv_from_priv()
275 {
276 #ifdef	HAVE_PRIV_SET
277 	return (priv_ineffect(PRIV_FILE_DAC_READ) &&
278 		    !priv_ineffect(PRIV_PROC_SETID));
279 #else
280 #ifdef	HAVE_LINUX_CAPS
281 	cap_t			cset;
282 	cap_flag_value_t	val = CAP_CLEAR;
283 
284 	cset = cap_get_proc();
285 
286 	cap_get_flag(cset, CAP_DAC_OVERRIDE, CAP_EFFECTIVE, &val);
287 	if (val == CAP_CLEAR)
288 		return (FALSE);
289 	val = CAP_SET;
290 	cap_get_flag(cset, CAP_SETUID, CAP_EFFECTIVE, &val);
291 	if (val == CAP_SET)
292 		return (FALSE);
293 	return (TRUE);
294 
295 #else	/* HAVE_LINUX_CAPS */
296 	return (FALSE);
297 #endif	/* HAVE_LINUX_CAPS */
298 #endif	/* HAVE_PRIV_SET */
299 }
300 #endif	/* defined(CDRECORD) || defined(READCD) */
301 
302 /*
303  * An attempt to implement an abstraction layer to detect fine grained
304  * privileges. This is not implemented in an efficient way (there are multiple
305  * syscalls to fetch the privileges from the kernel) but a few milliseconds
306  * should not count.
307  */
308 EXPORT BOOL
priv_eff_priv(pname)309 priv_eff_priv(pname)
310 	int	pname;
311 {
312 #if	!defined(HAVE_SETEUID) || !defined(HAVE_GETEUID)
313 	return (TRUE);
314 #else
315 #ifdef	HAVE_SOLARIS_PPRIV
316 #define	DID_PRIV
317 	switch (pname) {
318 
319 	case SCHILY_PRIV_FILE_DAC_READ:
320 		return (priv_eff(PRIV_FILE_DAC_READ));
321 	case SCHILY_PRIV_FILE_DAC_WRITE:
322 		return (priv_eff(PRIV_FILE_DAC_WRITE));
323 	case SCHILY_PRIV_SYS_DEVICES:
324 		return (priv_eff(PRIV_SYS_DEVICES));
325 	case SCHILY_PRIV_PROC_LOCK_MEMORY:
326 		return (priv_eff(PRIV_PROC_LOCK_MEMORY));
327 	case SCHILY_PRIV_PROC_PRIOCNTL:
328 		return (priv_eff(PRIV_PROC_PRIOCNTL));
329 	case SCHILY_PRIV_NET_PRIVADDR:
330 		return (priv_eff(PRIV_NET_PRIVADDR));
331 	}
332 #endif
333 #ifdef	HAVE_LINUX_CAPS
334 #define	DID_PRIV
335 	switch (pname) {
336 
337 	case SCHILY_PRIV_FILE_DAC_READ:
338 		return (priv_eff(CAP_DAC_READ_SEARCH) || priv_eff(CAP_DAC_OVERRIDE));
339 	case SCHILY_PRIV_FILE_DAC_WRITE:
340 		return (priv_eff(CAP_DAC_OVERRIDE));
341 	case SCHILY_PRIV_SYS_DEVICES:
342 		return (priv_eff(CAP_SYS_RAWIO) && priv_eff(CAP_SYS_ADMIN));
343 	case SCHILY_PRIV_PROC_LOCK_MEMORY:
344 		return (priv_eff(CAP_IPC_LOCK) && priv_eff(CAP_SYS_RESOURCE));
345 	case SCHILY_PRIV_PROC_PRIOCNTL:
346 		return (priv_eff(CAP_SYS_NICE));
347 	case SCHILY_PRIV_NET_PRIVADDR:
348 		return (priv_eff(CAP_NET_BIND_SERVICE));
349 	}
350 #endif
351 
352 #ifndef	DID_PRIV
353 	if (geteuid() == 0)
354 		return (TRUE);
355 #endif
356 	return (FALSE);
357 #endif
358 }
359 
360 #ifdef	HAVE_SOLARIS_PPRIV
361 LOCAL BOOL
priv_eff(pname)362 priv_eff(pname)
363 	const char	*pname;
364 {
365 	return (priv_ineffect(pname));
366 }
367 #endif
368 
369 #ifdef	HAVE_LINUX_CAPS
370 LOCAL BOOL
priv_eff(pname)371 priv_eff(pname)
372 	int		pname;
373 {
374 	cap_t			cset;
375 	cap_flag_value_t	val = CAP_CLEAR;
376 
377 	cset = cap_get_proc();
378 	cap_get_flag(cset, pname, CAP_EFFECTIVE, &val);
379 	if (val == CAP_CLEAR)
380 		return (FALSE);
381 	return (TRUE);
382 }
383 #endif
384 
385 #ifdef	HAVE_SOLARIS_PPRIV
386 #include <schily/varargs.h>
387 /*
388  * Allow to compile binaries that work on Solaris 11 build 140 and later
389  * as well as for older Solaris versions.
390  * If getpflags(PRIV_PFEXEC) fails, we have no in-kernel pfexec() and we
391  * just do nothing.
392  */
393 #ifndef	PRIV_PFEXEC
394 #define	PRIV_PFEXEC	0x0100
395 #endif
396 /*
397  * If PRIV_PFEXEC is defined, we have an in-kernel pfexec() that allows
398  * suid-root less installation and let programs gain the needed additional
399  * privileges even without a wrapper script.
400  */
401 /* VARARGS2 */
402 #ifdef	PROTOTYPES
403 EXPORT void
do_pfexec(int ac,char * av[],...)404 do_pfexec(int ac, char *av[], ...)
405 #else
406 EXPORT void
407 do_pfexec(ac, av, va_list)
408 	int	ac;
409 	char	*av[];
410 	va_decl
411 #endif
412 {
413 #ifdef	PRIV_PFEXEC
414 	va_list		args;
415 	BOOL		priv_ok = TRUE;
416 	priv_set_t	*pset;
417 	int		oflag;
418 	char		*av0;
419 	const char	*priv;
420 
421 	/*
422 	 * Avoid looping over execv().
423 	 * Return if we see our modified argv[0].
424 	 * If the first character of the last name component is a '+',
425 	 * just leave it as it is. We cannot restore the original character
426 	 * in this case. If it is an uppercase character, we assume
427 	 * that it was a translated lowercace character from our execv().
428 	 */
429 	av0 = strrchr(av[0], '/');
430 	if (av0 == NULL)
431 		av0 = av[0];
432 	else
433 		av0++;
434 	if (*av0 == '+')
435 		return;
436 	if (*av0 >= 'A' && *av0 <= 'Z') {
437 		*av0 = *av0 + 'a' - 'A';
438 		return;
439 	}
440 
441 	/*
442 	 * Check for the current privileges.
443 	 * Silently abort attempting to gain more privileges
444 	 * in case any error occurs.
445 	 */
446 	pset = priv_allocset();
447 	if (pset == NULL)
448 		return;
449 	if (getppriv(PRIV_EFFECTIVE, pset) < 0)
450 		return;
451 
452 	/*
453 	 * If we already have the needed privileges, we are done.
454 	 */
455 #ifdef	PROTOTYPES
456 	va_start(args, av);
457 #else
458 	va_start(args);
459 #endif
460 	while (priv_ok && (priv = va_arg(args, const char *)) != NULL) {
461 		if (!priv_ismember(pset, priv))
462 			priv_ok = FALSE;
463 	}
464 	va_end(args);
465 	priv_freeset(pset);
466 	if (priv_ok)
467 		return;
468 
469 	oflag = getpflags(PRIV_PFEXEC);
470 	if (oflag < 0)		/* Pre kernel-pfexec system? */
471 		return;
472 	if (oflag == 0) {	/* Kernel pfexec flag not yet set? */
473 		/*
474 		 * Set kernel pfexec flag.
475 		 * Return if this doesn't work for some reason.
476 		 */
477 		if (setpflags(PRIV_PFEXEC, 1) != 0) {
478 			return;
479 		}
480 	}
481 	/*
482 	 * Modify argv[0] to mark that we did already call execv().
483 	 * This is done in order to avoid infinite execv() loops caused by
484 	 * a missconfigured security system in /etc/security.
485 	 *
486 	 * In the usual case (a lowercase letter in the firsh character of the
487 	 * last pathname component), convert it to an uppercase character.
488 	 * Otherwise overwrite this character by a '+' sign.
489 	 */
490 	if (*av0 >= 'a' && *av0 <= 'z')
491 		*av0 = *av0 - 'a' + 'A';
492 	else
493 		*av0 = '+';
494 	execv(getexecname(), av);
495 #endif	/* PRIV_PFEXEC */
496 }
497 #endif
498