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