xref: /freebsd/sys/kern/kern_priv.c (revision a459a6cfe7ee2c9cc00fd2e8c0b6c3c32b8a8d2b)
1800c9408SRobert Watson /*-
28a36da99SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
38a36da99SPedro F. Giffuni  *
4800c9408SRobert Watson  * Copyright (c) 2006 nCircle Network Security, Inc.
56efcc2f2SRobert Watson  * Copyright (c) 2009 Robert N. M. Watson
6800c9408SRobert Watson  * All rights reserved.
7800c9408SRobert Watson  *
8800c9408SRobert Watson  * This software was developed by Robert N. M. Watson for the TrustedBSD
9800c9408SRobert Watson  * Project under contract to nCircle Network Security, Inc.
10800c9408SRobert Watson  *
11800c9408SRobert Watson  * Redistribution and use in source and binary forms, with or without
12800c9408SRobert Watson  * modification, are permitted provided that the following conditions
13800c9408SRobert Watson  * are met:
14800c9408SRobert Watson  * 1. Redistributions of source code must retain the above copyright
15800c9408SRobert Watson  *    notice, this list of conditions and the following disclaimer.
16800c9408SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
17800c9408SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
18800c9408SRobert Watson  *    documentation and/or other materials provided with the distribution.
19800c9408SRobert Watson  *
20800c9408SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21800c9408SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22800c9408SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23800c9408SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR, NCIRCLE NETWORK SECURITY,
24800c9408SRobert Watson  * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25800c9408SRobert Watson  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26800c9408SRobert Watson  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27800c9408SRobert Watson  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28800c9408SRobert Watson  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29800c9408SRobert Watson  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30800c9408SRobert Watson  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31800c9408SRobert Watson  */
32800c9408SRobert Watson 
33b916b56bSRobert Watson #include <sys/cdefs.h>
34b916b56bSRobert Watson __FBSDID("$FreeBSD$");
35b916b56bSRobert Watson 
36800c9408SRobert Watson #include <sys/param.h>
37800c9408SRobert Watson #include <sys/jail.h>
38800c9408SRobert Watson #include <sys/kernel.h>
39800c9408SRobert Watson #include <sys/priv.h>
40800c9408SRobert Watson #include <sys/proc.h>
416efcc2f2SRobert Watson #include <sys/sdt.h>
42800c9408SRobert Watson #include <sys/sysctl.h>
43800c9408SRobert Watson #include <sys/systm.h>
44800c9408SRobert Watson 
45800c9408SRobert Watson #include <security/mac/mac_framework.h>
46800c9408SRobert Watson 
47800c9408SRobert Watson /*
48800c9408SRobert Watson  * `suser_enabled' (which can be set by the security.bsd.suser_enabled
49800c9408SRobert Watson  * sysctl) determines whether the system 'super-user' policy is in effect.  If
50800c9408SRobert Watson  * it is nonzero, an effective uid of 0 connotes special privilege,
51800c9408SRobert Watson  * overriding many mandatory and discretionary protections.  If it is zero,
52800c9408SRobert Watson  * uid 0 is offered no special privilege in the kernel security policy.
53800c9408SRobert Watson  * Setting it to zero may seriously impact the functionality of many existing
54800c9408SRobert Watson  * userland programs, and should not be done without careful consideration of
55800c9408SRobert Watson  * the consequences.
56800c9408SRobert Watson  */
578bdcfb10SMateusz Guzik static int __read_mostly 	suser_enabled = 1;
58af3b2549SHans Petter Selasky SYSCTL_INT(_security_bsd, OID_AUTO, suser_enabled, CTLFLAG_RWTUN,
59800c9408SRobert Watson     &suser_enabled, 0, "processes with uid 0 have privilege");
60800c9408SRobert Watson 
615eb0d283SAndrey Zonov static int	unprivileged_mlock = 1;
62af3b2549SHans Petter Selasky SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_mlock, CTLFLAG_RWTUN,
635eb0d283SAndrey Zonov     &unprivileged_mlock, 0, "Allow non-root users to call mlock(2)");
645eb0d283SAndrey Zonov 
65b19d66fdSJamie Gritton static int	unprivileged_read_msgbuf = 1;
66b19d66fdSJamie Gritton SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf,
67b19d66fdSJamie Gritton     CTLFLAG_RW, &unprivileged_read_msgbuf, 0,
68b19d66fdSJamie Gritton     "Unprivileged processes may read the kernel message buffer");
69b19d66fdSJamie Gritton 
706efcc2f2SRobert Watson SDT_PROVIDER_DEFINE(priv);
71d9fae5abSAndriy Gapon SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__ok, "int");
72d9fae5abSAndriy Gapon SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__err, "int");
736efcc2f2SRobert Watson 
747b2ff0dcSMateusz Guzik static __always_inline int
757b2ff0dcSMateusz Guzik priv_check_cred_pre(struct ucred *cred, int priv)
767b2ff0dcSMateusz Guzik {
777b2ff0dcSMateusz Guzik 	int error;
787b2ff0dcSMateusz Guzik 
797b2ff0dcSMateusz Guzik #ifdef MAC
807b2ff0dcSMateusz Guzik 	error = mac_priv_check(cred, priv);
817b2ff0dcSMateusz Guzik #else
827b2ff0dcSMateusz Guzik 	error = 0;
837b2ff0dcSMateusz Guzik #endif
847b2ff0dcSMateusz Guzik 	return (error);
857b2ff0dcSMateusz Guzik }
867b2ff0dcSMateusz Guzik 
877b2ff0dcSMateusz Guzik static __always_inline int
887b2ff0dcSMateusz Guzik priv_check_cred_post(struct ucred *cred, int priv, int error, bool handled)
897b2ff0dcSMateusz Guzik {
907b2ff0dcSMateusz Guzik 
917b2ff0dcSMateusz Guzik 	if (__predict_true(handled))
927b2ff0dcSMateusz Guzik 		goto out;
937b2ff0dcSMateusz Guzik 	/*
947b2ff0dcSMateusz Guzik 	 * Now check with MAC, if enabled, to see if a policy module grants
957b2ff0dcSMateusz Guzik 	 * privilege.
967b2ff0dcSMateusz Guzik 	 */
977b2ff0dcSMateusz Guzik #ifdef MAC
987b2ff0dcSMateusz Guzik 	if (mac_priv_grant(cred, priv) == 0) {
997b2ff0dcSMateusz Guzik 		error = 0;
1007b2ff0dcSMateusz Guzik 		goto out;
1017b2ff0dcSMateusz Guzik 	}
1027b2ff0dcSMateusz Guzik #endif
1037b2ff0dcSMateusz Guzik 
1047b2ff0dcSMateusz Guzik 	/*
1057b2ff0dcSMateusz Guzik 	 * The default is deny, so if no policies have granted it, reject
1067b2ff0dcSMateusz Guzik 	 * with a privilege error here.
1077b2ff0dcSMateusz Guzik 	 */
1087b2ff0dcSMateusz Guzik 	error = EPERM;
1097b2ff0dcSMateusz Guzik out:
1107b2ff0dcSMateusz Guzik 	if (SDT_PROBES_ENABLED()) {
1117b2ff0dcSMateusz Guzik 		if (error)
1127b2ff0dcSMateusz Guzik 			SDT_PROBE1(priv, kernel, priv_check, priv__err, priv);
1137b2ff0dcSMateusz Guzik 		else
1147b2ff0dcSMateusz Guzik 			SDT_PROBE1(priv, kernel, priv_check, priv__ok, priv);
1157b2ff0dcSMateusz Guzik 	}
1167b2ff0dcSMateusz Guzik 	return (error);
1177b2ff0dcSMateusz Guzik }
1187b2ff0dcSMateusz Guzik 
119800c9408SRobert Watson /*
120800c9408SRobert Watson  * Check a credential for privilege.  Lots of good reasons to deny privilege;
121800c9408SRobert Watson  * only a few to grant it.
122800c9408SRobert Watson  */
123800c9408SRobert Watson int
124cc426dd3SMateusz Guzik priv_check_cred(struct ucred *cred, int priv)
125800c9408SRobert Watson {
126800c9408SRobert Watson 	int error;
127800c9408SRobert Watson 
128800c9408SRobert Watson 	KASSERT(PRIV_VALID(priv), ("priv_check_cred: invalid privilege %d",
129800c9408SRobert Watson 	    priv));
130800c9408SRobert Watson 
1317b2ff0dcSMateusz Guzik 	switch (priv) {
132*a459a6cfSMateusz Guzik 	case PRIV_VFS_LOOKUP:
133*a459a6cfSMateusz Guzik 		return (priv_check_cred_vfs_lookup(cred));
1347b2ff0dcSMateusz Guzik 	case PRIV_VFS_GENERATION:
1357b2ff0dcSMateusz Guzik 		return (priv_check_cred_vfs_generation(cred));
1367b2ff0dcSMateusz Guzik 	}
1377b2ff0dcSMateusz Guzik 
1387251b786SRobert Watson 	/*
1397251b786SRobert Watson 	 * We first evaluate policies that may deny the granting of
1407251b786SRobert Watson 	 * privilege unilaterally.
1417251b786SRobert Watson 	 */
1427b2ff0dcSMateusz Guzik 	error = priv_check_cred_pre(cred, priv);
143800c9408SRobert Watson 	if (error)
1446efcc2f2SRobert Watson 		goto out;
145800c9408SRobert Watson 
146800c9408SRobert Watson 	/*
147800c9408SRobert Watson 	 * Jail policy will restrict certain privileges that may otherwise be
148800c9408SRobert Watson 	 * be granted.
149800c9408SRobert Watson 	 */
150800c9408SRobert Watson 	error = prison_priv_check(cred, priv);
151800c9408SRobert Watson 	if (error)
1526efcc2f2SRobert Watson 		goto out;
153800c9408SRobert Watson 
1545eb0d283SAndrey Zonov 	if (unprivileged_mlock) {
1555eb0d283SAndrey Zonov 		/*
1565eb0d283SAndrey Zonov 		 * Allow unprivileged users to call mlock(2)/munlock(2) and
1575eb0d283SAndrey Zonov 		 * mlockall(2)/munlockall(2).
1585eb0d283SAndrey Zonov 		 */
1595eb0d283SAndrey Zonov 		switch (priv) {
1605eb0d283SAndrey Zonov 		case PRIV_VM_MLOCK:
1615eb0d283SAndrey Zonov 		case PRIV_VM_MUNLOCK:
1625eb0d283SAndrey Zonov 			error = 0;
1635eb0d283SAndrey Zonov 			goto out;
1645eb0d283SAndrey Zonov 		}
1655eb0d283SAndrey Zonov 	}
1665eb0d283SAndrey Zonov 
167b19d66fdSJamie Gritton 	if (unprivileged_read_msgbuf) {
168b19d66fdSJamie Gritton 		/*
169b19d66fdSJamie Gritton 		 * Allow an unprivileged user to read the kernel message
170b19d66fdSJamie Gritton 		 * buffer.
171b19d66fdSJamie Gritton 		 */
172b19d66fdSJamie Gritton 		if (priv == PRIV_MSGBUF) {
173b19d66fdSJamie Gritton 			error = 0;
174b19d66fdSJamie Gritton 			goto out;
175b19d66fdSJamie Gritton 		}
176b19d66fdSJamie Gritton 	}
177b19d66fdSJamie Gritton 
178800c9408SRobert Watson 	/*
179800c9408SRobert Watson 	 * Having determined if privilege is restricted by various policies,
1807251b786SRobert Watson 	 * now determine if privilege is granted.  At this point, any policy
1817251b786SRobert Watson 	 * may grant privilege.  For now, we allow short-circuit boolean
1827251b786SRobert Watson 	 * evaluation, so may not call all policies.  Perhaps we should.
183800c9408SRobert Watson 	 *
184800c9408SRobert Watson 	 * Superuser policy grants privilege based on the effective (or in
1857251b786SRobert Watson 	 * the case of specific privileges, real) uid being 0.  We allow the
1867251b786SRobert Watson 	 * superuser policy to be globally disabled, although this is
1877251b786SRobert Watson 	 * currenty of limited utility.
188800c9408SRobert Watson 	 */
189800c9408SRobert Watson 	if (suser_enabled) {
1907251b786SRobert Watson 		switch (priv) {
1917251b786SRobert Watson 		case PRIV_MAXFILES:
1927251b786SRobert Watson 		case PRIV_MAXPROC:
1937251b786SRobert Watson 		case PRIV_PROC_LIMIT:
1946efcc2f2SRobert Watson 			if (cred->cr_ruid == 0) {
1956efcc2f2SRobert Watson 				error = 0;
1966efcc2f2SRobert Watson 				goto out;
1976efcc2f2SRobert Watson 			}
1987251b786SRobert Watson 			break;
19963619b6dSKyle Evans 		case PRIV_VFS_READ_DIR:
20063619b6dSKyle Evans 			/*
20163619b6dSKyle Evans 			 * Allow PRIV_VFS_READ_DIR for root if we're not in a
20263619b6dSKyle Evans 			 * jail, otherwise deny unless a MAC policy grants it.
20363619b6dSKyle Evans 			 */
20463619b6dSKyle Evans 			if (jailed(cred))
20563619b6dSKyle Evans 				break;
20663619b6dSKyle Evans 			/* FALLTHROUGH */
2077251b786SRobert Watson 		default:
2086efcc2f2SRobert Watson 			if (cred->cr_uid == 0) {
2096efcc2f2SRobert Watson 				error = 0;
2106efcc2f2SRobert Watson 				goto out;
2116efcc2f2SRobert Watson 			}
2127251b786SRobert Watson 			break;
213800c9408SRobert Watson 		}
214800c9408SRobert Watson 	}
215800c9408SRobert Watson 
216800c9408SRobert Watson 	/*
2171e7df843SJamie Gritton 	 * Writes to kernel/physical memory are a typical root-only operation,
2181e7df843SJamie Gritton 	 * but non-root users are expected to be able to read it (provided they
2191e7df843SJamie Gritton 	 * have permission to access /dev/[k]mem).
220c71e3362SJamie Gritton 	 */
221c71e3362SJamie Gritton 	if (priv == PRIV_KMEM_READ) {
222c71e3362SJamie Gritton 		error = 0;
223c71e3362SJamie Gritton 		goto out;
224c71e3362SJamie Gritton 	}
225c71e3362SJamie Gritton 
226c71e3362SJamie Gritton 	/*
227b3079544SJamie Gritton 	 * Allow unprivileged process debugging on a per-jail basis.
228b3079544SJamie Gritton 	 * Do this here instead of prison_priv_check(), so it can also
229b3079544SJamie Gritton 	 * apply to prison0.
230b3079544SJamie Gritton 	 */
231b3079544SJamie Gritton 	if (priv == PRIV_DEBUG_UNPRIV) {
232b3079544SJamie Gritton 		if (prison_allow(cred, PR_ALLOW_UNPRIV_DEBUG)) {
233b3079544SJamie Gritton 			error = 0;
234b3079544SJamie Gritton 			goto out;
235b3079544SJamie Gritton 		}
236b3079544SJamie Gritton 	}
237b3079544SJamie Gritton 
2387b2ff0dcSMateusz Guzik 	return (priv_check_cred_post(cred, priv, error, false));
2396efcc2f2SRobert Watson out:
2407b2ff0dcSMateusz Guzik 	return (priv_check_cred_post(cred, priv, error, true));
241800c9408SRobert Watson }
242800c9408SRobert Watson 
243800c9408SRobert Watson int
244800c9408SRobert Watson priv_check(struct thread *td, int priv)
245800c9408SRobert Watson {
246800c9408SRobert Watson 
247800c9408SRobert Watson 	KASSERT(td == curthread, ("priv_check: td != curthread"));
248800c9408SRobert Watson 
249cc426dd3SMateusz Guzik 	return (priv_check_cred(td->td_ucred, priv));
250800c9408SRobert Watson }
2517b2ff0dcSMateusz Guzik 
25295275911SMateusz Guzik static int __noinline
253*a459a6cfSMateusz Guzik priv_check_cred_vfs_lookup_slow(struct ucred *cred)
254*a459a6cfSMateusz Guzik {
255*a459a6cfSMateusz Guzik 	int error;
256*a459a6cfSMateusz Guzik 
257*a459a6cfSMateusz Guzik 	error = priv_check_cred_pre(cred, PRIV_VFS_LOOKUP);
258*a459a6cfSMateusz Guzik 	if (error)
259*a459a6cfSMateusz Guzik 		goto out;
260*a459a6cfSMateusz Guzik 
261*a459a6cfSMateusz Guzik 	if (cred->cr_uid == 0 && suser_enabled) {
262*a459a6cfSMateusz Guzik 		error = 0;
263*a459a6cfSMateusz Guzik 		goto out;
264*a459a6cfSMateusz Guzik 	}
265*a459a6cfSMateusz Guzik 
266*a459a6cfSMateusz Guzik 	return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, false));
267*a459a6cfSMateusz Guzik out:
268*a459a6cfSMateusz Guzik 	return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, true));
269*a459a6cfSMateusz Guzik 
270*a459a6cfSMateusz Guzik }
271*a459a6cfSMateusz Guzik 
272*a459a6cfSMateusz Guzik int
273*a459a6cfSMateusz Guzik priv_check_cred_vfs_lookup(struct ucred *cred)
274*a459a6cfSMateusz Guzik {
275*a459a6cfSMateusz Guzik 	int error;
276*a459a6cfSMateusz Guzik 
277*a459a6cfSMateusz Guzik 	if (__predict_false(mac_priv_check_fp_flag ||
278*a459a6cfSMateusz Guzik 	    mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
279*a459a6cfSMateusz Guzik 		return (priv_check_cred_vfs_lookup_slow(cred));
280*a459a6cfSMateusz Guzik 
281*a459a6cfSMateusz Guzik 	error = EPERM;
282*a459a6cfSMateusz Guzik 	if (cred->cr_uid == 0 && suser_enabled)
283*a459a6cfSMateusz Guzik 		error = 0;
284*a459a6cfSMateusz Guzik 	return (error);
285*a459a6cfSMateusz Guzik }
286*a459a6cfSMateusz Guzik 
287*a459a6cfSMateusz Guzik int
288*a459a6cfSMateusz Guzik priv_check_cred_vfs_lookup_nomac(struct ucred *cred)
289*a459a6cfSMateusz Guzik {
290*a459a6cfSMateusz Guzik 	int error;
291*a459a6cfSMateusz Guzik 
292*a459a6cfSMateusz Guzik 	if (__predict_false(mac_priv_check_fp_flag ||
293*a459a6cfSMateusz Guzik 	    mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
294*a459a6cfSMateusz Guzik 		return (EAGAIN);
295*a459a6cfSMateusz Guzik 
296*a459a6cfSMateusz Guzik 	error = EPERM;
297*a459a6cfSMateusz Guzik 	if (cred->cr_uid == 0 && suser_enabled)
298*a459a6cfSMateusz Guzik 		error = 0;
299*a459a6cfSMateusz Guzik 	return (error);
300*a459a6cfSMateusz Guzik }
301*a459a6cfSMateusz Guzik 
302*a459a6cfSMateusz Guzik static int __noinline
30395275911SMateusz Guzik priv_check_cred_vfs_generation_slow(struct ucred *cred)
3047b2ff0dcSMateusz Guzik {
3057b2ff0dcSMateusz Guzik 	int error;
3067b2ff0dcSMateusz Guzik 
3077b2ff0dcSMateusz Guzik 	error = priv_check_cred_pre(cred, PRIV_VFS_GENERATION);
3087b2ff0dcSMateusz Guzik 	if (error)
3097b2ff0dcSMateusz Guzik 		goto out;
3107b2ff0dcSMateusz Guzik 
3117b2ff0dcSMateusz Guzik 	if (jailed(cred)) {
3127b2ff0dcSMateusz Guzik 		error = EPERM;
3137b2ff0dcSMateusz Guzik 		goto out;
3147b2ff0dcSMateusz Guzik 	}
3157b2ff0dcSMateusz Guzik 
3167b2ff0dcSMateusz Guzik 	if (cred->cr_uid == 0 && suser_enabled) {
3177b2ff0dcSMateusz Guzik 		error = 0;
3187b2ff0dcSMateusz Guzik 		goto out;
3197b2ff0dcSMateusz Guzik 	}
3207b2ff0dcSMateusz Guzik 
3217b2ff0dcSMateusz Guzik 	return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, false));
3227b2ff0dcSMateusz Guzik out:
3237b2ff0dcSMateusz Guzik 	return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, true));
3247b2ff0dcSMateusz Guzik 
3257b2ff0dcSMateusz Guzik }
32695275911SMateusz Guzik 
32795275911SMateusz Guzik int
32895275911SMateusz Guzik priv_check_cred_vfs_generation(struct ucred *cred)
32995275911SMateusz Guzik {
33095275911SMateusz Guzik 	int error;
33195275911SMateusz Guzik 
33295275911SMateusz Guzik 	if (__predict_false(mac_priv_check_fp_flag ||
33395275911SMateusz Guzik 	    mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
33495275911SMateusz Guzik 		return (priv_check_cred_vfs_generation_slow(cred));
33595275911SMateusz Guzik 
33695275911SMateusz Guzik 	error = EPERM;
33795275911SMateusz Guzik 	if (!jailed(cred) && cred->cr_uid == 0 && suser_enabled)
33895275911SMateusz Guzik 		error = 0;
33995275911SMateusz Guzik 	return (error);
34095275911SMateusz Guzik }
341