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