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/param.h>
35#include <sys/jail.h>
36#include <sys/kernel.h>
37#include <sys/lock.h>
38#include <sys/mutex.h>
39#include <sys/sx.h>
40#include <sys/priv.h>
41#include <sys/proc.h>
42#include <sys/sdt.h>
43#include <sys/sysctl.h>
44#include <sys/systm.h>
45
46#include <security/mac/mac_framework.h>
47
48/*
49 * `suser_enabled' (which can be set by the security.bsd.suser_enabled
50 * sysctl) determines whether the system 'super-user' policy is in effect.  If
51 * it is nonzero, an effective uid of 0 connotes special privilege,
52 * overriding many mandatory and discretionary protections.  If it is zero,
53 * uid 0 is offered no special privilege in the kernel security policy.
54 * Setting it to zero may seriously impact the functionality of many existing
55 * userland programs, and should not be done without careful consideration of
56 * the consequences.
57 */
58
59static bool
60suser_enabled(struct ucred *cred)
61{
62
63	return (prison_allow(cred, PR_ALLOW_SUSER));
64}
65
66static int
67sysctl_kern_suser_enabled(SYSCTL_HANDLER_ARGS)
68{
69	struct ucred *cred;
70	int error, enabled;
71
72	cred = req->td->td_ucred;
73	enabled = suser_enabled(cred);
74	error = sysctl_handle_int(oidp, &enabled, 0, req);
75	if (error || !req->newptr)
76		return (error);
77	prison_set_allow(cred, PR_ALLOW_SUSER, enabled);
78	return (0);
79}
80
81SYSCTL_PROC(_security_bsd, OID_AUTO, suser_enabled, CTLTYPE_INT |
82    CTLFLAG_RWTUN | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 0, 0,
83    &sysctl_kern_suser_enabled, "I", "Processes with uid 0 have privilege");
84
85static int	unprivileged_mlock = 1;
86SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_mlock, CTLFLAG_RWTUN,
87    &unprivileged_mlock, 0, "Allow non-root users to call mlock(2)");
88
89static int	unprivileged_read_msgbuf = 1;
90SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf,
91    CTLFLAG_RW, &unprivileged_read_msgbuf, 0,
92    "Unprivileged processes may read the kernel message buffer");
93
94SDT_PROVIDER_DEFINE(priv);
95SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__ok, "int");
96SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__err, "int");
97
98static __always_inline int
99priv_check_cred_pre(struct ucred *cred, int priv)
100{
101	int error;
102
103#ifdef MAC
104	error = mac_priv_check(cred, priv);
105#else
106	error = 0;
107#endif
108	return (error);
109}
110
111static __always_inline int
112priv_check_cred_post(struct ucred *cred, int priv, int error, bool handled)
113{
114
115	if (__predict_true(handled))
116		goto out;
117	/*
118	 * Now check with MAC, if enabled, to see if a policy module grants
119	 * privilege.
120	 */
121#ifdef MAC
122	if (mac_priv_grant(cred, priv) == 0) {
123		error = 0;
124		goto out;
125	}
126#endif
127
128	/*
129	 * The default is deny, so if no policies have granted it, reject
130	 * with a privilege error here.
131	 */
132	error = EPERM;
133out:
134	if (SDT_PROBES_ENABLED()) {
135		if (error)
136			SDT_PROBE1(priv, kernel, priv_check, priv__err, priv);
137		else
138			SDT_PROBE1(priv, kernel, priv_check, priv__ok, priv);
139	}
140	return (error);
141}
142
143/*
144 * Check a credential for privilege.  Lots of good reasons to deny privilege;
145 * only a few to grant it.
146 */
147int
148priv_check_cred(struct ucred *cred, int priv)
149{
150	int error;
151
152	KASSERT(PRIV_VALID(priv), ("priv_check_cred: invalid privilege %d",
153	    priv));
154
155	switch (priv) {
156	case PRIV_VFS_LOOKUP:
157		return (priv_check_cred_vfs_lookup(cred));
158	case PRIV_VFS_GENERATION:
159		return (priv_check_cred_vfs_generation(cred));
160	}
161
162	/*
163	 * We first evaluate policies that may deny the granting of
164	 * privilege unilaterally.
165	 */
166	error = priv_check_cred_pre(cred, priv);
167	if (error)
168		goto out;
169
170	/*
171	 * Jail policy will restrict certain privileges that may otherwise be
172	 * be granted.
173	 */
174	error = prison_priv_check(cred, priv);
175	if (error)
176		goto out;
177
178	if (unprivileged_mlock) {
179		/*
180		 * Allow unprivileged users to call mlock(2)/munlock(2) and
181		 * mlockall(2)/munlockall(2).
182		 */
183		switch (priv) {
184		case PRIV_VM_MLOCK:
185		case PRIV_VM_MUNLOCK:
186			error = 0;
187			goto out;
188		}
189	}
190
191	if (unprivileged_read_msgbuf) {
192		/*
193		 * Allow an unprivileged user to read the kernel message
194		 * buffer.
195		 */
196		if (priv == PRIV_MSGBUF) {
197			error = 0;
198			goto out;
199		}
200	}
201
202	/*
203	 * Having determined if privilege is restricted by various policies,
204	 * now determine if privilege is granted.  At this point, any policy
205	 * may grant privilege.  For now, we allow short-circuit boolean
206	 * evaluation, so may not call all policies.  Perhaps we should.
207	 *
208	 * Superuser policy grants privilege based on the effective (or in
209	 * the case of specific privileges, real) uid being 0.  We allow the
210	 * superuser policy to be globally disabled, although this is
211	 * currenty of limited utility.
212	 */
213	if (suser_enabled(cred)) {
214		switch (priv) {
215		case PRIV_MAXFILES:
216		case PRIV_MAXPROC:
217		case PRIV_PROC_LIMIT:
218			if (cred->cr_ruid == 0) {
219				error = 0;
220				goto out;
221			}
222			break;
223		case PRIV_VFS_READ_DIR:
224			/*
225			 * Allow PRIV_VFS_READ_DIR for root if we're not in a
226			 * jail, otherwise deny unless a MAC policy grants it.
227			 */
228			if (jailed(cred))
229				break;
230			/* FALLTHROUGH */
231		default:
232			if (cred->cr_uid == 0) {
233				error = 0;
234				goto out;
235			}
236			break;
237		}
238	}
239
240	/*
241	 * Writes to kernel/physical memory are a typical root-only operation,
242	 * but non-root users are expected to be able to read it (provided they
243	 * have permission to access /dev/[k]mem).
244	 */
245	if (priv == PRIV_KMEM_READ) {
246		error = 0;
247		goto out;
248	}
249
250	/*
251	 * Allow unprivileged process debugging on a per-jail basis.
252	 * Do this here instead of prison_priv_check(), so it can also
253	 * apply to prison0.
254	 */
255	if (priv == PRIV_DEBUG_UNPRIV) {
256		if (prison_allow(cred, PR_ALLOW_UNPRIV_DEBUG)) {
257			error = 0;
258			goto out;
259		}
260	}
261
262	return (priv_check_cred_post(cred, priv, error, false));
263out:
264	return (priv_check_cred_post(cred, priv, error, true));
265}
266
267int
268priv_check(struct thread *td, int priv)
269{
270
271	KASSERT(td == curthread, ("priv_check: td != curthread"));
272
273	return (priv_check_cred(td->td_ucred, priv));
274}
275
276static int __noinline
277priv_check_cred_vfs_lookup_slow(struct ucred *cred)
278{
279	int error;
280
281	error = priv_check_cred_pre(cred, PRIV_VFS_LOOKUP);
282	if (error)
283		goto out;
284
285	if (cred->cr_uid == 0 && suser_enabled(cred)) {
286		error = 0;
287		goto out;
288	}
289
290	return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, false));
291out:
292	return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, true));
293
294}
295
296int
297priv_check_cred_vfs_lookup(struct ucred *cred)
298{
299	int error;
300
301	if (__predict_false(mac_priv_check_fp_flag ||
302	    mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
303		return (priv_check_cred_vfs_lookup_slow(cred));
304
305	error = EPERM;
306	if (cred->cr_uid == 0 && suser_enabled(cred))
307		error = 0;
308	return (error);
309}
310
311int
312priv_check_cred_vfs_lookup_nomac(struct ucred *cred)
313{
314	int error;
315
316	if (__predict_false(mac_priv_check_fp_flag ||
317	    mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
318		return (EAGAIN);
319
320	error = EPERM;
321	if (cred->cr_uid == 0 && suser_enabled(cred))
322		error = 0;
323	return (error);
324}
325
326static int __noinline
327priv_check_cred_vfs_generation_slow(struct ucred *cred)
328{
329	int error;
330
331	error = priv_check_cred_pre(cred, PRIV_VFS_GENERATION);
332	if (error)
333		goto out;
334
335	if (jailed(cred)) {
336		error = EPERM;
337		goto out;
338	}
339
340	if (cred->cr_uid == 0 && suser_enabled(cred)) {
341		error = 0;
342		goto out;
343	}
344
345	return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, false));
346out:
347	return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, true));
348
349}
350
351int
352priv_check_cred_vfs_generation(struct ucred *cred)
353{
354	int error;
355
356	if (__predict_false(mac_priv_check_fp_flag ||
357	    mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
358		return (priv_check_cred_vfs_generation_slow(cred));
359
360	error = EPERM;
361	if (!jailed(cred) && cred->cr_uid == 0 && suser_enabled(cred))
362		error = 0;
363	return (error);
364}
365