xref: /freebsd/sys/kern/kern_priv.c (revision 2008043f386721d58158e37e0d7e50df8095942d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2006 nCircle Network Security, Inc.
5  * Copyright (c) 2009 Robert N. M. Watson
6  * Copyright (c) 2020 Mariusz Zaborski <oshogbo@FreeBSD.org>
7  * All rights reserved.
8  *
9  * This software was developed by Robert N. M. Watson for the TrustedBSD
10  * Project under contract to nCircle Network Security, Inc.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR, NCIRCLE NETWORK SECURITY,
25  * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 #include <sys/param.h>
36 #include <sys/jail.h>
37 #include <sys/kernel.h>
38 #include <sys/lock.h>
39 #include <sys/mutex.h>
40 #include <sys/sx.h>
41 #include <sys/priv.h>
42 #include <sys/proc.h>
43 #include <sys/sdt.h>
44 #include <sys/sysctl.h>
45 #include <sys/systm.h>
46 
47 #include <security/mac/mac_framework.h>
48 
49 /*
50  * `suser_enabled' (which can be set by the security.bsd.suser_enabled
51  * sysctl) determines whether the system 'super-user' policy is in effect.  If
52  * it is nonzero, an effective uid of 0 connotes special privilege,
53  * overriding many mandatory and discretionary protections.  If it is zero,
54  * uid 0 is offered no special privilege in the kernel security policy.
55  * Setting it to zero may seriously impact the functionality of many existing
56  * userland programs, and should not be done without careful consideration of
57  * the consequences.
58  */
59 
60 static bool
61 suser_enabled(struct ucred *cred)
62 {
63 
64 	return (prison_allow(cred, PR_ALLOW_SUSER));
65 }
66 
67 static int
68 sysctl_kern_suser_enabled(SYSCTL_HANDLER_ARGS)
69 {
70 	struct ucred *cred;
71 	int error, enabled;
72 
73 	cred = req->td->td_ucred;
74 	enabled = suser_enabled(cred);
75 	error = sysctl_handle_int(oidp, &enabled, 0, req);
76 	if (error || !req->newptr)
77 		return (error);
78 	prison_set_allow(cred, PR_ALLOW_SUSER, enabled);
79 	return (0);
80 }
81 
82 SYSCTL_PROC(_security_bsd, OID_AUTO, suser_enabled, CTLTYPE_INT |
83     CTLFLAG_RWTUN | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 0, 0,
84     &sysctl_kern_suser_enabled, "I", "Processes with uid 0 have privilege");
85 
86 static int	unprivileged_mlock = 1;
87 SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_mlock, CTLFLAG_RWTUN,
88     &unprivileged_mlock, 0, "Allow non-root users to call mlock(2)");
89 
90 static int	unprivileged_read_msgbuf = 1;
91 SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf,
92     CTLFLAG_RW, &unprivileged_read_msgbuf, 0,
93     "Unprivileged processes may read the kernel message buffer");
94 
95 SDT_PROVIDER_DEFINE(priv);
96 SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__ok, "int");
97 SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__err, "int");
98 
99 static __always_inline int
100 priv_check_cred_pre(struct ucred *cred, int priv)
101 {
102 	int error;
103 
104 #ifdef MAC
105 	error = mac_priv_check(cred, priv);
106 #else
107 	error = 0;
108 #endif
109 	return (error);
110 }
111 
112 static __always_inline int
113 priv_check_cred_post(struct ucred *cred, int priv, int error, bool handled)
114 {
115 
116 	if (__predict_true(handled))
117 		goto out;
118 	/*
119 	 * Now check with MAC, if enabled, to see if a policy module grants
120 	 * privilege.
121 	 */
122 #ifdef MAC
123 	if (mac_priv_grant(cred, priv) == 0) {
124 		error = 0;
125 		goto out;
126 	}
127 #endif
128 
129 	/*
130 	 * The default is deny, so if no policies have granted it, reject
131 	 * with a privilege error here.
132 	 */
133 	error = EPERM;
134 out:
135 	if (SDT_PROBES_ENABLED()) {
136 		if (error)
137 			SDT_PROBE1(priv, kernel, priv_check, priv__err, priv);
138 		else
139 			SDT_PROBE1(priv, kernel, priv_check, priv__ok, priv);
140 	}
141 	return (error);
142 }
143 
144 /*
145  * Check a credential for privilege.  Lots of good reasons to deny privilege;
146  * only a few to grant it.
147  */
148 int
149 priv_check_cred(struct ucred *cred, int priv)
150 {
151 	int error;
152 
153 	KASSERT(PRIV_VALID(priv), ("priv_check_cred: invalid privilege %d",
154 	    priv));
155 
156 	switch (priv) {
157 	case PRIV_VFS_LOOKUP:
158 		return (priv_check_cred_vfs_lookup(cred));
159 	case PRIV_VFS_GENERATION:
160 		return (priv_check_cred_vfs_generation(cred));
161 	}
162 
163 	/*
164 	 * We first evaluate policies that may deny the granting of
165 	 * privilege unilaterally.
166 	 */
167 	error = priv_check_cred_pre(cred, priv);
168 	if (error)
169 		goto out;
170 
171 	/*
172 	 * Jail policy will restrict certain privileges that may otherwise be
173 	 * be granted.
174 	 */
175 	error = prison_priv_check(cred, priv);
176 	if (error)
177 		goto out;
178 
179 	if (unprivileged_mlock) {
180 		/*
181 		 * Allow unprivileged users to call mlock(2)/munlock(2) and
182 		 * mlockall(2)/munlockall(2).
183 		 */
184 		switch (priv) {
185 		case PRIV_VM_MLOCK:
186 		case PRIV_VM_MUNLOCK:
187 			error = 0;
188 			goto out;
189 		}
190 	}
191 
192 	if (unprivileged_read_msgbuf) {
193 		/*
194 		 * Allow an unprivileged user to read the kernel message
195 		 * buffer.
196 		 */
197 		if (priv == PRIV_MSGBUF) {
198 			error = 0;
199 			goto out;
200 		}
201 	}
202 
203 	/*
204 	 * Having determined if privilege is restricted by various policies,
205 	 * now determine if privilege is granted.  At this point, any policy
206 	 * may grant privilege.  For now, we allow short-circuit boolean
207 	 * evaluation, so may not call all policies.  Perhaps we should.
208 	 *
209 	 * Superuser policy grants privilege based on the effective (or in
210 	 * the case of specific privileges, real) uid being 0.  We allow the
211 	 * superuser policy to be globally disabled, although this is
212 	 * currenty of limited utility.
213 	 */
214 	if (suser_enabled(cred)) {
215 		switch (priv) {
216 		case PRIV_MAXFILES:
217 		case PRIV_MAXPROC:
218 		case PRIV_PROC_LIMIT:
219 			if (cred->cr_ruid == 0) {
220 				error = 0;
221 				goto out;
222 			}
223 			break;
224 		case PRIV_VFS_READ_DIR:
225 			/*
226 			 * Allow PRIV_VFS_READ_DIR for root if we're not in a
227 			 * jail, otherwise deny unless a MAC policy grants it.
228 			 */
229 			if (jailed(cred))
230 				break;
231 			/* FALLTHROUGH */
232 		default:
233 			if (cred->cr_uid == 0) {
234 				error = 0;
235 				goto out;
236 			}
237 			break;
238 		}
239 	}
240 
241 	/*
242 	 * Writes to kernel/physical memory are a typical root-only operation,
243 	 * but non-root users are expected to be able to read it (provided they
244 	 * have permission to access /dev/[k]mem).
245 	 */
246 	if (priv == PRIV_KMEM_READ) {
247 		error = 0;
248 		goto out;
249 	}
250 
251 	/*
252 	 * Allow unprivileged process debugging on a per-jail basis.
253 	 * Do this here instead of prison_priv_check(), so it can also
254 	 * apply to prison0.
255 	 */
256 	if (priv == PRIV_DEBUG_UNPRIV) {
257 		if (prison_allow(cred, PR_ALLOW_UNPRIV_DEBUG)) {
258 			error = 0;
259 			goto out;
260 		}
261 	}
262 
263 	return (priv_check_cred_post(cred, priv, error, false));
264 out:
265 	return (priv_check_cred_post(cred, priv, error, true));
266 }
267 
268 int
269 priv_check(struct thread *td, int priv)
270 {
271 
272 	KASSERT(td == curthread, ("priv_check: td != curthread"));
273 
274 	return (priv_check_cred(td->td_ucred, priv));
275 }
276 
277 static int __noinline
278 priv_check_cred_vfs_lookup_slow(struct ucred *cred)
279 {
280 	int error;
281 
282 	error = priv_check_cred_pre(cred, PRIV_VFS_LOOKUP);
283 	if (error)
284 		goto out;
285 
286 	if (cred->cr_uid == 0 && suser_enabled(cred)) {
287 		error = 0;
288 		goto out;
289 	}
290 
291 	return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, false));
292 out:
293 	return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, true));
294 
295 }
296 
297 int
298 priv_check_cred_vfs_lookup(struct ucred *cred)
299 {
300 	int error;
301 
302 	if (__predict_false(mac_priv_check_fp_flag ||
303 	    mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
304 		return (priv_check_cred_vfs_lookup_slow(cred));
305 
306 	error = EPERM;
307 	if (cred->cr_uid == 0 && suser_enabled(cred))
308 		error = 0;
309 	return (error);
310 }
311 
312 int
313 priv_check_cred_vfs_lookup_nomac(struct ucred *cred)
314 {
315 	int error;
316 
317 	if (__predict_false(mac_priv_check_fp_flag ||
318 	    mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
319 		return (EAGAIN);
320 
321 	error = EPERM;
322 	if (cred->cr_uid == 0 && suser_enabled(cred))
323 		error = 0;
324 	return (error);
325 }
326 
327 static int __noinline
328 priv_check_cred_vfs_generation_slow(struct ucred *cred)
329 {
330 	int error;
331 
332 	error = priv_check_cred_pre(cred, PRIV_VFS_GENERATION);
333 	if (error)
334 		goto out;
335 
336 	if (jailed(cred)) {
337 		error = EPERM;
338 		goto out;
339 	}
340 
341 	if (cred->cr_uid == 0 && suser_enabled(cred)) {
342 		error = 0;
343 		goto out;
344 	}
345 
346 	return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, false));
347 out:
348 	return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, true));
349 
350 }
351 
352 int
353 priv_check_cred_vfs_generation(struct ucred *cred)
354 {
355 	int error;
356 
357 	if (__predict_false(mac_priv_check_fp_flag ||
358 	    mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
359 		return (priv_check_cred_vfs_generation_slow(cred));
360 
361 	error = EPERM;
362 	if (!jailed(cred) && cred->cr_uid == 0 && suser_enabled(cred))
363 		error = 0;
364 	return (error);
365 }
366