xref: /illumos-gate/usr/src/cmd/bhyve/privileges.c (revision 2817ebc2366683391690bbd0e0dd2e82aaa281f7)
1*2817ebc2SAndy Fiddaman /*
2*2817ebc2SAndy Fiddaman  * This file and its contents are supplied under the terms of the
3*2817ebc2SAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
4*2817ebc2SAndy Fiddaman  * You may only use this file in accordance with the terms of version
5*2817ebc2SAndy Fiddaman  * 1.0 of the CDDL.
6*2817ebc2SAndy Fiddaman  *
7*2817ebc2SAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
8*2817ebc2SAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
9*2817ebc2SAndy Fiddaman  * http://www.illumos.org/license/CDDL.
10*2817ebc2SAndy Fiddaman  */
11*2817ebc2SAndy Fiddaman 
12*2817ebc2SAndy Fiddaman /*
13*2817ebc2SAndy Fiddaman  * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
14*2817ebc2SAndy Fiddaman  */
15*2817ebc2SAndy Fiddaman 
16*2817ebc2SAndy Fiddaman /*
17*2817ebc2SAndy Fiddaman  * Two privilege sets are maintained. One which is the minimal privileges
18*2817ebc2SAndy Fiddaman  * necessary to run bhyve - 'bhyve_priv_min' - and one which is the maximum
19*2817ebc2SAndy Fiddaman  * privileges required - 'bhyve_priv_max'. This second set starts off identical
20*2817ebc2SAndy Fiddaman  * to the first, and is added to by modules during initialisation depending on
21*2817ebc2SAndy Fiddaman  * their requirements and their configuration.
22*2817ebc2SAndy Fiddaman  * Once all modules are initialised, and before the VM is set running, the
23*2817ebc2SAndy Fiddaman  * privileges are locked by setting the 'Permitted' privilege set from the
24*2817ebc2SAndy Fiddaman  * contents of the maximum set, which is now the upper bound, and dropping back
25*2817ebc2SAndy Fiddaman  * to the minimum set.
26*2817ebc2SAndy Fiddaman  *
27*2817ebc2SAndy Fiddaman  * Privileges are process wide, and this framework does not support threaded
28*2817ebc2SAndy Fiddaman  * access. The ID of the thread that first initialises privileges is recorded,
29*2817ebc2SAndy Fiddaman  * and all subsequent privilege operations must be done by the same thread.
30*2817ebc2SAndy Fiddaman  */
31*2817ebc2SAndy Fiddaman 
32*2817ebc2SAndy Fiddaman #include <err.h>
33*2817ebc2SAndy Fiddaman #include <pthread.h>
34*2817ebc2SAndy Fiddaman #include <priv.h>
35*2817ebc2SAndy Fiddaman #include <stdlib.h>
36*2817ebc2SAndy Fiddaman #include <string.h>
37*2817ebc2SAndy Fiddaman #include <upanic.h>
38*2817ebc2SAndy Fiddaman #include <sys/debug.h>
39*2817ebc2SAndy Fiddaman #include "config.h"
40*2817ebc2SAndy Fiddaman #include "debug.h"
41*2817ebc2SAndy Fiddaman #include "privileges.h"
42*2817ebc2SAndy Fiddaman 
43*2817ebc2SAndy Fiddaman static pthread_t priv_thread;
44*2817ebc2SAndy Fiddaman static priv_set_t *bhyve_priv_init;
45*2817ebc2SAndy Fiddaman static priv_set_t *bhyve_priv_min;
46*2817ebc2SAndy Fiddaman static priv_set_t *bhyve_priv_max;
47*2817ebc2SAndy Fiddaman static bool priv_debug = false;
48*2817ebc2SAndy Fiddaman 
49*2817ebc2SAndy Fiddaman #define	DPRINTF(params) \
50*2817ebc2SAndy Fiddaman     if (priv_debug) do { PRINTLN params; fflush(stdout); } while (0)
51*2817ebc2SAndy Fiddaman 
52*2817ebc2SAndy Fiddaman static void
illumos_priv_printset(const char * tag,priv_set_t * set)53*2817ebc2SAndy Fiddaman illumos_priv_printset(const char *tag, priv_set_t *set)
54*2817ebc2SAndy Fiddaman {
55*2817ebc2SAndy Fiddaman 	char *s;
56*2817ebc2SAndy Fiddaman 
57*2817ebc2SAndy Fiddaman 	s = priv_set_to_str(set, ',', PRIV_STR_LIT);
58*2817ebc2SAndy Fiddaman 	if (s == NULL) {
59*2817ebc2SAndy Fiddaman 		warn("priv_set_to_str(%s) failed", tag);
60*2817ebc2SAndy Fiddaman 		return;
61*2817ebc2SAndy Fiddaman 	}
62*2817ebc2SAndy Fiddaman 	DPRINTF((" + %s: %s", tag, s));
63*2817ebc2SAndy Fiddaman 	free(s);
64*2817ebc2SAndy Fiddaman }
65*2817ebc2SAndy Fiddaman 
66*2817ebc2SAndy Fiddaman void
illumos_priv_reduce(void)67*2817ebc2SAndy Fiddaman illumos_priv_reduce(void)
68*2817ebc2SAndy Fiddaman {
69*2817ebc2SAndy Fiddaman 	VERIFY3S(pthread_equal(pthread_self(), priv_thread), !=, 0);
70*2817ebc2SAndy Fiddaman 	DPRINTF((" + Reducing privileges to minimum"));
71*2817ebc2SAndy Fiddaman 	if (setppriv(PRIV_SET, PRIV_EFFECTIVE, bhyve_priv_min) != 0)
72*2817ebc2SAndy Fiddaman 		err(4, "failed to reduce privileges");
73*2817ebc2SAndy Fiddaman }
74*2817ebc2SAndy Fiddaman 
75*2817ebc2SAndy Fiddaman static void
illumos_priv_add_set(priv_set_t * set,const char * priv,const char * src)76*2817ebc2SAndy Fiddaman illumos_priv_add_set(priv_set_t *set, const char *priv, const char *src)
77*2817ebc2SAndy Fiddaman {
78*2817ebc2SAndy Fiddaman 	VERIFY3S(pthread_equal(pthread_self(), priv_thread), !=, 0);
79*2817ebc2SAndy Fiddaman 	DPRINTF((" + Adding privilege %s (%s)", priv, src));
80*2817ebc2SAndy Fiddaman 	if (!priv_ismember(bhyve_priv_init, priv))
81*2817ebc2SAndy Fiddaman 		errx(4, "Privilege %s (for %s) not in initial set", priv, src);
82*2817ebc2SAndy Fiddaman 	if (priv_addset(set, priv) != 0)
83*2817ebc2SAndy Fiddaman 		err(4, "Failed to add %s privilege for %s", priv, src);
84*2817ebc2SAndy Fiddaman }
85*2817ebc2SAndy Fiddaman void
illumos_priv_add(const char * priv,const char * src)86*2817ebc2SAndy Fiddaman illumos_priv_add(const char *priv, const char *src)
87*2817ebc2SAndy Fiddaman {
88*2817ebc2SAndy Fiddaman 	illumos_priv_add_set(bhyve_priv_max, priv, src);
89*2817ebc2SAndy Fiddaman }
90*2817ebc2SAndy Fiddaman 
91*2817ebc2SAndy Fiddaman void
illumos_priv_add_min(const char * priv,const char * src)92*2817ebc2SAndy Fiddaman illumos_priv_add_min(const char *priv, const char *src)
93*2817ebc2SAndy Fiddaman {
94*2817ebc2SAndy Fiddaman 	illumos_priv_add_set(bhyve_priv_min, priv, src);
95*2817ebc2SAndy Fiddaman 	illumos_priv_add_set(bhyve_priv_max, priv, src);
96*2817ebc2SAndy Fiddaman }
97*2817ebc2SAndy Fiddaman 
98*2817ebc2SAndy Fiddaman void
illumos_priv_init(void)99*2817ebc2SAndy Fiddaman illumos_priv_init(void)
100*2817ebc2SAndy Fiddaman {
101*2817ebc2SAndy Fiddaman 	priv_debug = get_config_bool_default("privileges.debug", false);
102*2817ebc2SAndy Fiddaman 
103*2817ebc2SAndy Fiddaman 	DPRINTF((" + Initialising privileges."));
104*2817ebc2SAndy Fiddaman 
105*2817ebc2SAndy Fiddaman 	if (priv_debug)
106*2817ebc2SAndy Fiddaman 		(void) setpflags(PRIV_DEBUG, 1);
107*2817ebc2SAndy Fiddaman 
108*2817ebc2SAndy Fiddaman 	priv_thread = pthread_self();
109*2817ebc2SAndy Fiddaman 
110*2817ebc2SAndy Fiddaman 	if ((bhyve_priv_init = priv_allocset()) == NULL)
111*2817ebc2SAndy Fiddaman 		err(4, "failed to allocate memory for initial priv set");
112*2817ebc2SAndy Fiddaman 	if ((bhyve_priv_min = priv_allocset()) == NULL)
113*2817ebc2SAndy Fiddaman 		err(4, "failed to allocate memory for minimum priv set");
114*2817ebc2SAndy Fiddaman 	if ((bhyve_priv_max = priv_allocset()) == NULL)
115*2817ebc2SAndy Fiddaman 		err(4, "failed to allocate memory for maximum priv set");
116*2817ebc2SAndy Fiddaman 
117*2817ebc2SAndy Fiddaman 	if (getppriv(PRIV_EFFECTIVE, bhyve_priv_init) != 0)
118*2817ebc2SAndy Fiddaman 		err(4, "failed to fetch current privileges");
119*2817ebc2SAndy Fiddaman 
120*2817ebc2SAndy Fiddaman 	if (priv_debug)
121*2817ebc2SAndy Fiddaman 		illumos_priv_printset("initial", bhyve_priv_init);
122*2817ebc2SAndy Fiddaman 
123*2817ebc2SAndy Fiddaman 	/*
124*2817ebc2SAndy Fiddaman 	 * file_read is left in the minimum set to allow for lazy library
125*2817ebc2SAndy Fiddaman 	 * loading.
126*2817ebc2SAndy Fiddaman 	 */
127*2817ebc2SAndy Fiddaman 	priv_basicset(bhyve_priv_min);
128*2817ebc2SAndy Fiddaman 	VERIFY0(priv_delset(bhyve_priv_min, PRIV_FILE_LINK_ANY));
129*2817ebc2SAndy Fiddaman 	VERIFY0(priv_delset(bhyve_priv_min, PRIV_FILE_WRITE));
130*2817ebc2SAndy Fiddaman 	VERIFY0(priv_delset(bhyve_priv_min, PRIV_NET_ACCESS));
131*2817ebc2SAndy Fiddaman 	VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_EXEC));
132*2817ebc2SAndy Fiddaman 	VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_FORK));
133*2817ebc2SAndy Fiddaman 	VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_INFO));
134*2817ebc2SAndy Fiddaman 	VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_SECFLAGS));
135*2817ebc2SAndy Fiddaman 	VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_SESSION));
136*2817ebc2SAndy Fiddaman 
137*2817ebc2SAndy Fiddaman 	priv_intersect(bhyve_priv_init, bhyve_priv_min);
138*2817ebc2SAndy Fiddaman 	priv_copyset(bhyve_priv_min, bhyve_priv_max);
139*2817ebc2SAndy Fiddaman 
140*2817ebc2SAndy Fiddaman 	/*
141*2817ebc2SAndy Fiddaman 	 * These are privileges that we know will always be needed.
142*2817ebc2SAndy Fiddaman 	 * Other privileges may be added by modules as necessary during
143*2817ebc2SAndy Fiddaman 	 * initialisation.
144*2817ebc2SAndy Fiddaman 	 */
145*2817ebc2SAndy Fiddaman 
146*2817ebc2SAndy Fiddaman 	illumos_priv_add(PRIV_FILE_WRITE, "init");
147*2817ebc2SAndy Fiddaman 
148*2817ebc2SAndy Fiddaman 	/*
149*2817ebc2SAndy Fiddaman 	 * bhyve can work without proc_clock_highres so don't enforce that
150*2817ebc2SAndy Fiddaman 	 * it is present.
151*2817ebc2SAndy Fiddaman 	 */
152*2817ebc2SAndy Fiddaman 	if (priv_ismember(bhyve_priv_init, PRIV_PROC_CLOCK_HIGHRES))
153*2817ebc2SAndy Fiddaman 		illumos_priv_add_min(PRIV_PROC_CLOCK_HIGHRES, "init");
154*2817ebc2SAndy Fiddaman 	else
155*2817ebc2SAndy Fiddaman 		warnx("The 'proc_clock_highres' privilege is not available");
156*2817ebc2SAndy Fiddaman }
157*2817ebc2SAndy Fiddaman 
158*2817ebc2SAndy Fiddaman void
illumos_priv_lock(void)159*2817ebc2SAndy Fiddaman illumos_priv_lock(void)
160*2817ebc2SAndy Fiddaman {
161*2817ebc2SAndy Fiddaman 	VERIFY3S(pthread_equal(pthread_self(), priv_thread), !=, 0);
162*2817ebc2SAndy Fiddaman 
163*2817ebc2SAndy Fiddaman 	if (bhyve_priv_init == NULL) {
164*2817ebc2SAndy Fiddaman 		const char *msg = "attempted to re-lock privileges";
165*2817ebc2SAndy Fiddaman 		upanic(msg, strlen(msg));
166*2817ebc2SAndy Fiddaman 	}
167*2817ebc2SAndy Fiddaman 
168*2817ebc2SAndy Fiddaman 	priv_intersect(bhyve_priv_init, bhyve_priv_max);
169*2817ebc2SAndy Fiddaman 
170*2817ebc2SAndy Fiddaman 	if (priv_debug) {
171*2817ebc2SAndy Fiddaman 		DPRINTF((" + Locking privileges"));
172*2817ebc2SAndy Fiddaman 
173*2817ebc2SAndy Fiddaman 		illumos_priv_printset("min", bhyve_priv_min);
174*2817ebc2SAndy Fiddaman 		illumos_priv_printset("max", bhyve_priv_max);
175*2817ebc2SAndy Fiddaman 	}
176*2817ebc2SAndy Fiddaman 
177*2817ebc2SAndy Fiddaman 	if (setppriv(PRIV_SET, PRIV_PERMITTED, bhyve_priv_max) != 0) {
178*2817ebc2SAndy Fiddaman 		const char *fail = "failed to reduce permitted privileges";
179*2817ebc2SAndy Fiddaman 		upanic(fail, strlen(fail));
180*2817ebc2SAndy Fiddaman 	}
181*2817ebc2SAndy Fiddaman 
182*2817ebc2SAndy Fiddaman 	if (setppriv(PRIV_SET, PRIV_LIMIT, bhyve_priv_max) != 0) {
183*2817ebc2SAndy Fiddaman 		const char *fail = "failed to reduce limit privileges";
184*2817ebc2SAndy Fiddaman 		upanic(fail, strlen(fail));
185*2817ebc2SAndy Fiddaman 	}
186*2817ebc2SAndy Fiddaman 
187*2817ebc2SAndy Fiddaman 	illumos_priv_reduce();
188*2817ebc2SAndy Fiddaman 
189*2817ebc2SAndy Fiddaman 	priv_freeset(bhyve_priv_init);
190*2817ebc2SAndy Fiddaman 	bhyve_priv_init = NULL;
191*2817ebc2SAndy Fiddaman }
192