1 /*-
2  * Copyright (c) 2006-2011 Varnish Software AS
3  * Copyright 2011-2020 UPLEX - Nils Goroll Systemoptimierung
4  * All rights reserved.
5  *
6  * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7  *	   Nils Goroll <nils.goroll@uplex.de>
8  *
9  * SPDX-License-Identifier: BSD-2-Clause
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * "Jailing" *1) child processes on Solaris and Solaris-derivatives *2)
33  * ====================================================================
34  *
35  * *1) The name is motivated by the availability of the -j command line
36  *     option. Jailing Varnish is not to be confused with BSD Jails or
37  *     Solaris Zones.
38  *
39  *     In Solaris parlour, jail == least privileges
40  *
41  * *2) e.g. illumos, SmartOS, OmniOS etc.
42  *
43  *
44  * Note on use of symbolic PRIV_* constants
45  * ----------------------------------------
46  *
47  * We assume backwards compatibility only for Solaris Releases after the
48  * OpenSolaris Launch. For privileges which existed at the time of the
49  * OpenSolaris Launch, we use the constants from sys/priv_names.h and assert
50  * that priv_addset must succeed.
51  *
52  * For privileges which have been added later, we need to use priv strings in
53  * order not to break builds of varnish on older platforms. To remain binary
54  * compatible, we can't assert that priv_addset succeeds, but we may assert that
55  * it either succeeds or fails with EINVAL.
56  *
57  * See priv_setop_check()
58  *
59  * Note on introduction of new privileges (or: lack of forward compatibility)
60  * --------------------------------------------------------------------------
61  *
62  * For optimal build and binary forward compatibility, we could use subtractive
63  * set specs like
64  *
65  *       basic,!file_link_any,!proc_exec,!proc_fork,!proc_info,!proc_session
66  *
67  * which would implicitly keep any privileges newly introduced to the 'basic'
68  * set.
69  *
70  * But we have a preference for making an informed decision about which
71  * privileges varnish subprocesses should have, so we prefer to risk breaking
72  * varnish temporarily on newer kernels and be notified of missing privileges
73  * through bug reports.
74  *
75  * Notes on the SNOCD flag
76  * -----------------------
77  *
78  * On Solaris, any uid/gid fiddling which can be interpreted as 'waiving
79  * privileges' will lead to the processes' SNOCD flag being set, disabling core
80  * dumps unless explicitly allowed using coreadm (see below). There is no
81  * equivalent to Linux PR_SET_DUMPABLE. The only way to clear the flag is a call
82  * to some form of exec(). The presence of the SNOCD flag also prevents many
83  * process manipulations from other processes with the same uid/gid unless the
84  * latter have the proc_owner privilege.
85  *
86  * Thus, if we want to run subprocesses with a different uid/gid than the master
87  * process, we cannot avoid the SNOCD flag for those subprocesses not exec'ing
88  * (VCC, VCLLOAD, WORKER).
89  *
90  *
91  * We should, however, avoid to accidentally set the SNOCD flag when setting
92  * privileges (see https://www.varnish-cache.org/trac/ticket/671 )
93  *
94  * When changing the logic herein, always check with mdb -k. Replace _PID_ with
95  * the pid of your varnish child, the result should be 0, otherwise a regression
96  * has been introduced.
97  *
98  * > 0t_PID_::pid2proc | ::print proc_t p_flag | >a
99  * > (<a & 0x10000000)=X
100  *		0
101  *
102  * (a value of 0x10000000 indicates that SNOCD is set)
103  *
104  * How to get core dumps of the worker process on Solaris
105  * ------------------------------------------------------
106  *
107  * (see previous paragraph for explanation).
108  *
109  * Two options:
110  *
111  * - start the varnish master process under the same user/group given for the -u
112  *   / -g command line option and elevated privileges but without proc_setid,
113  *   e.g.:
114  *
115  *	pfexec ppriv -e -s A=basic,net_privaddr,sys_resource varnishd ...
116  *
117  * - allow coredumps of setid processes (ignoring SNOCD)
118  *
119  *   See coreadm(1M) - global-setid / proc-setid
120  *
121  * brief history of privileges introduced since OpenSolaris Launch
122  * ---------------------------------------------------------------
123  *
124  * (from hg log -gp usr/src/uts/common/os/priv_defs
125  *    or git log -p usr/src/uts/common/os/priv_defs)
126  *
127  * ARC cases are not necessarily accurate (induced from commit msg)
128  *
129  * privileges used here marked with *
130  *
131  * Illumos ticket
132  * ARC case	    hg/git commit  first release
133  *
134  * PSARC/2006/155?  37f4a3e2bd99   onnv_37
135  * - file_downgrade_sl
136  * - file_upgrade_sl
137  * - net_bindmlp
138  * - net_mac_aware
139  * - sys_trans_label
140  * - win_colormap
141  * - win_config
142  * - win_dac_read
143  * - win_dac_write
144  * - win_devices
145  * - win_dga
146  * - win_downgrade_sl
147  * - win_fontpath
148  * - win_mac_read
149  * - win_mac_write
150  * - win_selection
151  * - win_upgrade_sl
152  *
153  * PSARC/2006/218   5dbf296c1e57   onnv_39
154  * - graphics_access
155  * - graphics_map
156  *
157  * PSARC/2006/366   aaf16568054b   onnv_57
158  * - net_config
159  *
160  * PSARC/2007/315?  3047ad28a67b   onnv_77
161  * - file_flag_set
162  *
163  * PSARC/2007/560?  3047ad28a67b   onnv_77
164  * - sys_smb
165  *
166  * PSARC 2008/046   47f6aa7a8077   onnv_85
167  * - contract_identify
168  *
169  * PSARC 2008/289   79a9dac325d9   onnv_92
170  * - virt_manage
171  * - xvm_control
172  *
173  * PSARC 2008/473   eff7960d93cd   onnv_98
174  * - sys_dl_config
175  *
176  * PSARC/2006/475   faf256d5c16c   onnv_103
177  * - net_observability
178  *
179  * PSARC/2009/317   8e29565352fc   onnv_117
180  * - sys_ppp_config
181  *
182  * PSARC/2009/373   3be00c4a6835   onnv_125
183  * - sys_iptun_config
184  *
185  * PSARC/2008/252   e209937a4f19   onnv_128
186  * - net_mac_implicit
187  *
188  * PSARC/2009/685   8eca52188202   onnv_132
189  * * net_access
190  *
191  * PSARC/2009/378   63678502e95e   onnv_140
192  * * file_read
193  * * file_write
194  *
195  * PSARC/2010/181   15439b11d535   onnv_142
196  * - sys_res_bind
197  *
198  * unknown	    unknown	   Solaris11
199  * - sys_flow_config
200  * - sys_share
201  *
202  * IL3923	    24d819e6779c   Illumos
203  * - proc_prioup
204  *
205  */
206 
207 //lint -e{766}
208 #include "config.h"
209 
210 #ifdef HAVE_SETPPRIV
211 
212 #include <stdio.h>	// ARG_ERR
213 #include <stdlib.h>
214 #include <string.h>
215 #include <unistd.h>
216 
217 #include "mgt/mgt.h"
218 
219 #include "common/heritage.h"
220 
221 #ifdef HAVE_PRIV_H
222 #include <priv.h>
223 #endif
224 
225 /* renamed from sys/priv_const.h */
226 #define VJS_EFFECTIVE		0
227 #define VJS_INHERITABLE		1
228 #define VJS_PERMITTED		2
229 #define VJS_LIMIT		3
230 
231 #define VJS_NSET		(VJS_LIMIT + 1)
232 
233 #define VJS_MASK(x) (1U << (x))
234 
235 /* to denote sharing */
236 #define JAIL_MASTER_ANY 0
237 
238 const priv_ptype_t vjs_ptype[VJS_NSET] = {
239 	[VJS_EFFECTIVE]		= PRIV_EFFECTIVE,
240 	[VJS_INHERITABLE]	= PRIV_INHERITABLE,
241 	[VJS_PERMITTED]		= PRIV_PERMITTED,
242 	[VJS_LIMIT]		= PRIV_LIMIT
243 };
244 
245 static priv_set_t *vjs_sets[JAIL_LIMIT][VJS_NSET];
246 static priv_set_t *vjs_inverse[JAIL_LIMIT][VJS_NSET];
247 static priv_set_t *vjs_proc_setid;	// for vjs_setuid
248 
249 static void v_matchproto_(jail_master_f)
250 	vjs_master(enum jail_master_e jme);
251 
252 /*------------------------------------------------------------*/
253 
254 static inline int
priv_setop_check(int a)255 priv_setop_check(int a)
256 {
257 	if (a == 0)
258 		return (1);
259 	if (errno == EINVAL)
260 		return (1);
261 	return (0);
262 }
263 
264 #define priv_setop_assert(a) assert(priv_setop_check(a))
265 
266 /*------------------------------------------------------------*/
267 
268 static int
vjs_priv_on(int vs,priv_set_t ** set)269 vjs_priv_on(int vs, priv_set_t **set)
270 {
271 	assert(vs >= 0);
272 	assert(vs < VJS_NSET);
273 
274 	return (setppriv(PRIV_ON, vjs_ptype[vs], set[vs]));
275 }
276 
277 /* ------------------------------------------------------------
278  * initialization of privilege sets from mgt_jail_solaris_tbl.h
279  * and implicit rules documented therein
280  */
281 
282 static inline void
vjs_add(priv_set_t * sets[VJS_NSET],unsigned mask,const char * priv)283 vjs_add(priv_set_t *sets[VJS_NSET], unsigned mask, const char *priv)
284 {
285 	int i;
286 	for (i = 0; i < VJS_NSET; i++)
287 		if (mask & VJS_MASK(i))
288 			priv_setop_assert(priv_addset(sets[i], priv));
289 }
290 
291 /* add SUBPROC INHERITABLE and PERMITTED to MASTER PERMITTED */
292 static int
vjs_master_rules(void)293 vjs_master_rules(void)
294 {
295 	priv_set_t *punion = priv_allocset();
296 	int vs, vj;
297 
298 	AN(punion);
299 
300 	for (vs = VJS_INHERITABLE; vs <= VJS_PERMITTED; vs ++) {
301 		priv_emptyset(punion);
302 		for (vj = JAIL_SUBPROC; vj < JAIL_LIMIT; vj++)
303 			priv_union(vjs_sets[vj][vs], punion);
304 		priv_union(punion, vjs_sets[JAIL_MASTER_ANY][VJS_PERMITTED]);
305 	}
306 
307 	priv_freeset(punion);
308 
309 	return (0);
310 }
311 
312 static priv_set_t *
vjs_alloc(void)313 vjs_alloc(void)
314 {
315 	priv_set_t *s;
316 
317 	s = priv_allocset();
318 	AN(s);
319 	priv_emptyset(s);
320 	return (s);
321 }
322 
v_matchproto_(jail_init_f)323 static int v_matchproto_(jail_init_f)
324 vjs_init(char **args)
325 {
326 	priv_set_t **sets, *permitted, *inheritable, *user = NULL;
327 	const char *e;
328 	int vj, vs;
329 
330 	if (args != NULL && *args != NULL) {
331 		for (;*args != NULL; args++) {
332 			if (!strncmp(*args, "worker=", 7)) {
333 				user = priv_str_to_set((*args) + 7, ",", &e);
334 				if (user == NULL)
335 					ARGV_ERR(
336 					    "-jsolaris: parsing worker= "
337 					    "argument failed near %s.\n",
338 					    e);
339 				continue;
340 			}
341 			ARGV_ERR("-jsolaris: unknown sub-argument '%s'\n",
342 			    *args);
343 		}
344 	}
345 
346 	permitted = vjs_alloc();
347 	AN(permitted);
348 	AZ(getppriv(PRIV_PERMITTED, permitted));
349 
350 	inheritable = vjs_alloc();
351 	AN(inheritable);
352 	AZ(getppriv(PRIV_INHERITABLE, inheritable));
353 	priv_union(permitted, inheritable);
354 
355 	/* init privset for vjs_setuid() */
356 	vjs_proc_setid = vjs_alloc();
357 	AN(vjs_proc_setid);
358 	priv_setop_assert(priv_addset(vjs_proc_setid, PRIV_PROC_SETID));
359 
360 	assert(JAIL_MASTER_ANY < JAIL_SUBPROC);
361 	/* alloc privsets.
362 	 * for master, PERMITTED and LIMIT are shared
363 	 */
364 	for (vj = 0; vj < JAIL_SUBPROC; vj++)
365 		for (vs = 0; vs < VJS_NSET; vs++) {
366 			if (vj == JAIL_MASTER_ANY || vs < VJS_PERMITTED) {
367 				vjs_sets[vj][vs] = vjs_alloc();
368 				vjs_inverse[vj][vs] = vjs_alloc();
369 			} else {
370 				vjs_sets[vj][vs] =
371 					vjs_sets[JAIL_MASTER_ANY][vs];
372 				vjs_inverse[vj][vs] =
373 					vjs_inverse[JAIL_MASTER_ANY][vs];
374 			}
375 		}
376 
377 	for (; vj < JAIL_LIMIT; vj++)
378 		for (vs = 0; vs < VJS_NSET; vs++) {
379 			vjs_sets[vj][vs] = vjs_alloc();
380 			vjs_inverse[vj][vs] = vjs_alloc();
381 		}
382 
383 	/* init from table */
384 #define PRIV(name, mask, priv) vjs_add(vjs_sets[JAIL_ ## name], mask, priv);
385 #include "mgt_jail_solaris_tbl.h"
386 
387 	if (user != NULL)
388 		priv_union(user, vjs_sets[JAIL_SUBPROC_WORKER][VJS_EFFECTIVE]);
389 
390 	/* mask by available privs */
391 	for (vj = 0; vj < JAIL_LIMIT; vj++) {
392 		sets = vjs_sets[vj];
393 		priv_intersect(permitted, sets[VJS_EFFECTIVE]);
394 		priv_intersect(permitted, sets[VJS_PERMITTED]);
395 		priv_intersect(inheritable, sets[VJS_INHERITABLE]);
396 	}
397 
398 	/* SUBPROC implicit rules */
399 	for (vj = JAIL_SUBPROC; vj < JAIL_LIMIT; vj++) {
400 		sets = vjs_sets[vj];
401 		priv_union(sets[VJS_EFFECTIVE], sets[VJS_PERMITTED]);
402 		priv_union(sets[VJS_PERMITTED], sets[VJS_LIMIT]);
403 		priv_union(sets[VJS_INHERITABLE], sets[VJS_LIMIT]);
404 	}
405 
406 	vjs_master_rules();
407 
408 	/* MASTER implicit rules */
409 	for (vj = 0; vj < JAIL_SUBPROC; vj++) {
410 		sets = vjs_sets[vj];
411 		priv_union(sets[VJS_EFFECTIVE], sets[VJS_PERMITTED]);
412 		priv_union(sets[VJS_PERMITTED], sets[VJS_LIMIT]);
413 		priv_union(sets[VJS_INHERITABLE], sets[VJS_LIMIT]);
414 	}
415 
416 	/* generate inverse */
417 	for (vj = 0; vj < JAIL_LIMIT; vj++)
418 		for (vs = 0; vs < VJS_NSET; vs++) {
419 			priv_copyset(vjs_sets[vj][vs], vjs_inverse[vj][vs]);
420 			priv_inverse(vjs_inverse[vj][vs]);
421 		}
422 
423 	vjs_master(JAIL_MASTER_LOW);
424 
425 	priv_freeset(permitted);
426 	priv_freeset(inheritable);
427 	/* XXX LEAK: no _fini for priv_freeset() */
428 	return (0);
429 }
430 
431 static void
vjs_waive(int jail)432 vjs_waive(int jail)
433 {
434 	priv_set_t **sets;
435 	int i;
436 
437 	assert(jail >= 0);
438 	assert(jail < JAIL_LIMIT);
439 
440 	sets = vjs_inverse[jail];
441 
442 	for (i = 0; i < VJS_NSET; i++)
443 		AZ(setppriv(PRIV_OFF, vjs_ptype[i], sets[i]));
444 }
445 
446 static void
vjs_setuid(void)447 vjs_setuid(void)
448 {
449 	if (priv_ineffect(PRIV_PROC_SETID)) {
450 		if (getgid() != mgt_param.gid)
451 			XXXAZ(setgid(mgt_param.gid));
452 		if (getuid() != mgt_param.uid)
453 			XXXAZ(setuid(mgt_param.uid));
454 		AZ(setppriv(PRIV_OFF, PRIV_EFFECTIVE, vjs_proc_setid));
455 		AZ(setppriv(PRIV_OFF, PRIV_PERMITTED, vjs_proc_setid));
456 	} else {
457 		MGT_Complain(C_SECURITY,
458 		    "Privilege %s missing, will not change uid/gid",
459 		    PRIV_PROC_SETID);
460 	}
461 }
462 
v_matchproto_(jail_subproc_f)463 static void v_matchproto_(jail_subproc_f)
464 vjs_subproc(enum jail_subproc_e jse)
465 {
466 
467 	AZ(vjs_priv_on(VJS_EFFECTIVE, vjs_sets[jse]));
468 	AZ(vjs_priv_on(VJS_INHERITABLE, vjs_sets[jse]));
469 
470 	vjs_setuid();
471 	vjs_waive(jse);
472 }
473 
v_matchproto_(jail_master_f)474 static void v_matchproto_(jail_master_f)
475 vjs_master(enum jail_master_e jme)
476 {
477 
478 	assert(jme < JAIL_SUBPROC);
479 
480 	AZ(vjs_priv_on(VJS_EFFECTIVE, vjs_sets[jme]));
481 	AZ(vjs_priv_on(VJS_INHERITABLE, vjs_sets[jme]));
482 
483 	vjs_waive(jme);
484 }
485 
486 const struct jail_tech jail_tech_solaris = {
487 	.magic =	JAIL_TECH_MAGIC,
488 	.name =		"solaris",
489 	.init =		vjs_init,
490 	.master =	vjs_master,
491 //	.make_workdir =	vjs_make_workdir,
492 //	.storage_file =	vjs_storage_file,
493 	.subproc =	vjs_subproc,
494 };
495 
496 #endif /* HAVE_SETPPRIV */
497