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