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