1800c9408SRobert Watson /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
38a36da99SPedro F. Giffuni *
4800c9408SRobert Watson * Copyright (c) 2006 nCircle Network Security, Inc.
56efcc2f2SRobert Watson * Copyright (c) 2009 Robert N. M. Watson
605e1e482SMariusz Zaborski * Copyright (c) 2020 Mariusz Zaborski <oshogbo@FreeBSD.org>
7800c9408SRobert Watson * All rights reserved.
8800c9408SRobert Watson *
9800c9408SRobert Watson * This software was developed by Robert N. M. Watson for the TrustedBSD
10800c9408SRobert Watson * Project under contract to nCircle Network Security, Inc.
11800c9408SRobert Watson *
12800c9408SRobert Watson * Redistribution and use in source and binary forms, with or without
13800c9408SRobert Watson * modification, are permitted provided that the following conditions
14800c9408SRobert Watson * are met:
15800c9408SRobert Watson * 1. Redistributions of source code must retain the above copyright
16800c9408SRobert Watson * notice, this list of conditions and the following disclaimer.
17800c9408SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
18800c9408SRobert Watson * notice, this list of conditions and the following disclaimer in the
19800c9408SRobert Watson * documentation and/or other materials provided with the distribution.
20800c9408SRobert Watson *
21800c9408SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22800c9408SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23800c9408SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24800c9408SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR, NCIRCLE NETWORK SECURITY,
25800c9408SRobert Watson * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26800c9408SRobert Watson * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27800c9408SRobert Watson * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28800c9408SRobert Watson * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29800c9408SRobert Watson * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30800c9408SRobert Watson * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31800c9408SRobert Watson * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32800c9408SRobert Watson */
33800c9408SRobert Watson
34800c9408SRobert Watson #include <sys/param.h>
35800c9408SRobert Watson #include <sys/jail.h>
36800c9408SRobert Watson #include <sys/kernel.h>
3705e1e482SMariusz Zaborski #include <sys/lock.h>
3805e1e482SMariusz Zaborski #include <sys/mutex.h>
3905e1e482SMariusz Zaborski #include <sys/sx.h>
40800c9408SRobert Watson #include <sys/priv.h>
41800c9408SRobert Watson #include <sys/proc.h>
426efcc2f2SRobert Watson #include <sys/sdt.h>
43800c9408SRobert Watson #include <sys/sysctl.h>
44800c9408SRobert Watson #include <sys/systm.h>
45800c9408SRobert Watson
46800c9408SRobert Watson #include <security/mac/mac_framework.h>
47800c9408SRobert Watson
48800c9408SRobert Watson /*
49800c9408SRobert Watson * `suser_enabled' (which can be set by the security.bsd.suser_enabled
50800c9408SRobert Watson * sysctl) determines whether the system 'super-user' policy is in effect. If
51800c9408SRobert Watson * it is nonzero, an effective uid of 0 connotes special privilege,
52800c9408SRobert Watson * overriding many mandatory and discretionary protections. If it is zero,
53800c9408SRobert Watson * uid 0 is offered no special privilege in the kernel security policy.
54800c9408SRobert Watson * Setting it to zero may seriously impact the functionality of many existing
55800c9408SRobert Watson * userland programs, and should not be done without careful consideration of
56800c9408SRobert Watson * the consequences.
57800c9408SRobert Watson */
5805e1e482SMariusz Zaborski
5905e1e482SMariusz Zaborski static bool
suser_enabled(struct ucred * cred)6005e1e482SMariusz Zaborski suser_enabled(struct ucred *cred)
6105e1e482SMariusz Zaborski {
6205e1e482SMariusz Zaborski
630fe74ae6SJamie Gritton return (prison_allow(cred, PR_ALLOW_SUSER));
6405e1e482SMariusz Zaborski }
6505e1e482SMariusz Zaborski
6605e1e482SMariusz Zaborski static int
sysctl_kern_suser_enabled(SYSCTL_HANDLER_ARGS)6705e1e482SMariusz Zaborski sysctl_kern_suser_enabled(SYSCTL_HANDLER_ARGS)
6805e1e482SMariusz Zaborski {
6905e1e482SMariusz Zaborski struct ucred *cred;
700fe74ae6SJamie Gritton int error, enabled;
7105e1e482SMariusz Zaborski
7205e1e482SMariusz Zaborski cred = req->td->td_ucred;
7305e1e482SMariusz Zaborski enabled = suser_enabled(cred);
7405e1e482SMariusz Zaborski error = sysctl_handle_int(oidp, &enabled, 0, req);
7505e1e482SMariusz Zaborski if (error || !req->newptr)
7605e1e482SMariusz Zaborski return (error);
770fe74ae6SJamie Gritton prison_set_allow(cred, PR_ALLOW_SUSER, enabled);
7805e1e482SMariusz Zaborski return (0);
7905e1e482SMariusz Zaborski }
8005e1e482SMariusz Zaborski
8105e1e482SMariusz Zaborski SYSCTL_PROC(_security_bsd, OID_AUTO, suser_enabled, CTLTYPE_INT |
82f488d5b7SMariusz Zaborski CTLFLAG_RWTUN | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 0, 0,
83f488d5b7SMariusz Zaborski &sysctl_kern_suser_enabled, "I", "Processes with uid 0 have privilege");
84800c9408SRobert Watson
855eb0d283SAndrey Zonov static int unprivileged_mlock = 1;
86af3b2549SHans Petter Selasky SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_mlock, CTLFLAG_RWTUN,
875eb0d283SAndrey Zonov &unprivileged_mlock, 0, "Allow non-root users to call mlock(2)");
885eb0d283SAndrey Zonov
89b19d66fdSJamie Gritton static int unprivileged_read_msgbuf = 1;
90b19d66fdSJamie Gritton SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf,
91b19d66fdSJamie Gritton CTLFLAG_RW, &unprivileged_read_msgbuf, 0,
92b19d66fdSJamie Gritton "Unprivileged processes may read the kernel message buffer");
93b19d66fdSJamie Gritton
946efcc2f2SRobert Watson SDT_PROVIDER_DEFINE(priv);
95d9fae5abSAndriy Gapon SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__ok, "int");
96d9fae5abSAndriy Gapon SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__err, "int");
976efcc2f2SRobert Watson
987b2ff0dcSMateusz Guzik static __always_inline int
priv_check_cred_pre(struct ucred * cred,int priv)997b2ff0dcSMateusz Guzik priv_check_cred_pre(struct ucred *cred, int priv)
1007b2ff0dcSMateusz Guzik {
1017b2ff0dcSMateusz Guzik int error;
1027b2ff0dcSMateusz Guzik
1037b2ff0dcSMateusz Guzik #ifdef MAC
1047b2ff0dcSMateusz Guzik error = mac_priv_check(cred, priv);
1057b2ff0dcSMateusz Guzik #else
1067b2ff0dcSMateusz Guzik error = 0;
1077b2ff0dcSMateusz Guzik #endif
1087b2ff0dcSMateusz Guzik return (error);
1097b2ff0dcSMateusz Guzik }
1107b2ff0dcSMateusz Guzik
1117b2ff0dcSMateusz Guzik static __always_inline int
priv_check_cred_post(struct ucred * cred,int priv,int error,bool handled)1127b2ff0dcSMateusz Guzik priv_check_cred_post(struct ucred *cred, int priv, int error, bool handled)
1137b2ff0dcSMateusz Guzik {
1147b2ff0dcSMateusz Guzik
1157b2ff0dcSMateusz Guzik if (__predict_true(handled))
1167b2ff0dcSMateusz Guzik goto out;
1177b2ff0dcSMateusz Guzik /*
1187b2ff0dcSMateusz Guzik * Now check with MAC, if enabled, to see if a policy module grants
1197b2ff0dcSMateusz Guzik * privilege.
1207b2ff0dcSMateusz Guzik */
1217b2ff0dcSMateusz Guzik #ifdef MAC
1227b2ff0dcSMateusz Guzik if (mac_priv_grant(cred, priv) == 0) {
1237b2ff0dcSMateusz Guzik error = 0;
1247b2ff0dcSMateusz Guzik goto out;
1257b2ff0dcSMateusz Guzik }
1267b2ff0dcSMateusz Guzik #endif
1277b2ff0dcSMateusz Guzik
1287b2ff0dcSMateusz Guzik /*
1297b2ff0dcSMateusz Guzik * The default is deny, so if no policies have granted it, reject
1307b2ff0dcSMateusz Guzik * with a privilege error here.
1317b2ff0dcSMateusz Guzik */
1327b2ff0dcSMateusz Guzik error = EPERM;
1337b2ff0dcSMateusz Guzik out:
1347b2ff0dcSMateusz Guzik if (SDT_PROBES_ENABLED()) {
1357b2ff0dcSMateusz Guzik if (error)
1367b2ff0dcSMateusz Guzik SDT_PROBE1(priv, kernel, priv_check, priv__err, priv);
1377b2ff0dcSMateusz Guzik else
1387b2ff0dcSMateusz Guzik SDT_PROBE1(priv, kernel, priv_check, priv__ok, priv);
1397b2ff0dcSMateusz Guzik }
1407b2ff0dcSMateusz Guzik return (error);
1417b2ff0dcSMateusz Guzik }
1427b2ff0dcSMateusz Guzik
143800c9408SRobert Watson /*
144800c9408SRobert Watson * Check a credential for privilege. Lots of good reasons to deny privilege;
145800c9408SRobert Watson * only a few to grant it.
146800c9408SRobert Watson */
147800c9408SRobert Watson int
priv_check_cred(struct ucred * cred,int priv)148cc426dd3SMateusz Guzik priv_check_cred(struct ucred *cred, int priv)
149800c9408SRobert Watson {
150800c9408SRobert Watson int error;
151800c9408SRobert Watson
152800c9408SRobert Watson KASSERT(PRIV_VALID(priv), ("priv_check_cred: invalid privilege %d",
153800c9408SRobert Watson priv));
154800c9408SRobert Watson
1557b2ff0dcSMateusz Guzik switch (priv) {
156a459a6cfSMateusz Guzik case PRIV_VFS_LOOKUP:
157a459a6cfSMateusz Guzik return (priv_check_cred_vfs_lookup(cred));
1587b2ff0dcSMateusz Guzik case PRIV_VFS_GENERATION:
1597b2ff0dcSMateusz Guzik return (priv_check_cred_vfs_generation(cred));
1607b2ff0dcSMateusz Guzik }
1617b2ff0dcSMateusz Guzik
1627251b786SRobert Watson /*
1637251b786SRobert Watson * We first evaluate policies that may deny the granting of
1647251b786SRobert Watson * privilege unilaterally.
1657251b786SRobert Watson */
1667b2ff0dcSMateusz Guzik error = priv_check_cred_pre(cred, priv);
167800c9408SRobert Watson if (error)
1686efcc2f2SRobert Watson goto out;
169800c9408SRobert Watson
170800c9408SRobert Watson /*
171800c9408SRobert Watson * Jail policy will restrict certain privileges that may otherwise be
172800c9408SRobert Watson * be granted.
173800c9408SRobert Watson */
174800c9408SRobert Watson error = prison_priv_check(cred, priv);
175800c9408SRobert Watson if (error)
1766efcc2f2SRobert Watson goto out;
177800c9408SRobert Watson
1785eb0d283SAndrey Zonov if (unprivileged_mlock) {
1795eb0d283SAndrey Zonov /*
1805eb0d283SAndrey Zonov * Allow unprivileged users to call mlock(2)/munlock(2) and
1815eb0d283SAndrey Zonov * mlockall(2)/munlockall(2).
1825eb0d283SAndrey Zonov */
1835eb0d283SAndrey Zonov switch (priv) {
1845eb0d283SAndrey Zonov case PRIV_VM_MLOCK:
1855eb0d283SAndrey Zonov case PRIV_VM_MUNLOCK:
1865eb0d283SAndrey Zonov error = 0;
1875eb0d283SAndrey Zonov goto out;
1885eb0d283SAndrey Zonov }
1895eb0d283SAndrey Zonov }
1905eb0d283SAndrey Zonov
191b19d66fdSJamie Gritton if (unprivileged_read_msgbuf) {
192b19d66fdSJamie Gritton /*
193b19d66fdSJamie Gritton * Allow an unprivileged user to read the kernel message
194b19d66fdSJamie Gritton * buffer.
195b19d66fdSJamie Gritton */
196b19d66fdSJamie Gritton if (priv == PRIV_MSGBUF) {
197b19d66fdSJamie Gritton error = 0;
198b19d66fdSJamie Gritton goto out;
199b19d66fdSJamie Gritton }
200b19d66fdSJamie Gritton }
201b19d66fdSJamie Gritton
202800c9408SRobert Watson /*
203800c9408SRobert Watson * Having determined if privilege is restricted by various policies,
2047251b786SRobert Watson * now determine if privilege is granted. At this point, any policy
2057251b786SRobert Watson * may grant privilege. For now, we allow short-circuit boolean
2067251b786SRobert Watson * evaluation, so may not call all policies. Perhaps we should.
207800c9408SRobert Watson *
208800c9408SRobert Watson * Superuser policy grants privilege based on the effective (or in
2097251b786SRobert Watson * the case of specific privileges, real) uid being 0. We allow the
2107251b786SRobert Watson * superuser policy to be globally disabled, although this is
2117251b786SRobert Watson * currenty of limited utility.
212800c9408SRobert Watson */
21305e1e482SMariusz Zaborski if (suser_enabled(cred)) {
2147251b786SRobert Watson switch (priv) {
2157251b786SRobert Watson case PRIV_MAXFILES:
2167251b786SRobert Watson case PRIV_MAXPROC:
2177251b786SRobert Watson case PRIV_PROC_LIMIT:
2186efcc2f2SRobert Watson if (cred->cr_ruid == 0) {
2196efcc2f2SRobert Watson error = 0;
2206efcc2f2SRobert Watson goto out;
2216efcc2f2SRobert Watson }
2227251b786SRobert Watson break;
22363619b6dSKyle Evans case PRIV_VFS_READ_DIR:
22463619b6dSKyle Evans /*
22563619b6dSKyle Evans * Allow PRIV_VFS_READ_DIR for root if we're not in a
22663619b6dSKyle Evans * jail, otherwise deny unless a MAC policy grants it.
22763619b6dSKyle Evans */
22863619b6dSKyle Evans if (jailed(cred))
22963619b6dSKyle Evans break;
23063619b6dSKyle Evans /* FALLTHROUGH */
2317251b786SRobert Watson default:
2326efcc2f2SRobert Watson if (cred->cr_uid == 0) {
2336efcc2f2SRobert Watson error = 0;
2346efcc2f2SRobert Watson goto out;
2356efcc2f2SRobert Watson }
2367251b786SRobert Watson break;
237800c9408SRobert Watson }
238800c9408SRobert Watson }
239800c9408SRobert Watson
240800c9408SRobert Watson /*
2411e7df843SJamie Gritton * Writes to kernel/physical memory are a typical root-only operation,
2421e7df843SJamie Gritton * but non-root users are expected to be able to read it (provided they
2431e7df843SJamie Gritton * have permission to access /dev/[k]mem).
244c71e3362SJamie Gritton */
245*4a5fa108SSimon J. Gerraty switch (priv) {
246*4a5fa108SSimon J. Gerraty case PRIV_KMEM_READ:
247*4a5fa108SSimon J. Gerraty case PRIV_PROC_MEM_WRITE: /* we already checked candebug */
248c71e3362SJamie Gritton error = 0;
249c71e3362SJamie Gritton goto out;
250c71e3362SJamie Gritton }
251c71e3362SJamie Gritton
252c71e3362SJamie Gritton /*
253b3079544SJamie Gritton * Allow unprivileged process debugging on a per-jail basis.
254b3079544SJamie Gritton * Do this here instead of prison_priv_check(), so it can also
255b3079544SJamie Gritton * apply to prison0.
256b3079544SJamie Gritton */
257b3079544SJamie Gritton if (priv == PRIV_DEBUG_UNPRIV) {
258b3079544SJamie Gritton if (prison_allow(cred, PR_ALLOW_UNPRIV_DEBUG)) {
259b3079544SJamie Gritton error = 0;
260b3079544SJamie Gritton goto out;
261b3079544SJamie Gritton }
262b3079544SJamie Gritton }
263b3079544SJamie Gritton
2647b2ff0dcSMateusz Guzik return (priv_check_cred_post(cred, priv, error, false));
2656efcc2f2SRobert Watson out:
2667b2ff0dcSMateusz Guzik return (priv_check_cred_post(cred, priv, error, true));
267800c9408SRobert Watson }
268800c9408SRobert Watson
269800c9408SRobert Watson int
priv_check(struct thread * td,int priv)270800c9408SRobert Watson priv_check(struct thread *td, int priv)
271800c9408SRobert Watson {
272800c9408SRobert Watson
273800c9408SRobert Watson KASSERT(td == curthread, ("priv_check: td != curthread"));
274800c9408SRobert Watson
275cc426dd3SMateusz Guzik return (priv_check_cred(td->td_ucred, priv));
276800c9408SRobert Watson }
2777b2ff0dcSMateusz Guzik
27895275911SMateusz Guzik static int __noinline
priv_check_cred_vfs_lookup_slow(struct ucred * cred)279a459a6cfSMateusz Guzik priv_check_cred_vfs_lookup_slow(struct ucred *cred)
280a459a6cfSMateusz Guzik {
281a459a6cfSMateusz Guzik int error;
282a459a6cfSMateusz Guzik
283a459a6cfSMateusz Guzik error = priv_check_cred_pre(cred, PRIV_VFS_LOOKUP);
284a459a6cfSMateusz Guzik if (error)
285a459a6cfSMateusz Guzik goto out;
286a459a6cfSMateusz Guzik
28705e1e482SMariusz Zaborski if (cred->cr_uid == 0 && suser_enabled(cred)) {
288a459a6cfSMateusz Guzik error = 0;
289a459a6cfSMateusz Guzik goto out;
290a459a6cfSMateusz Guzik }
291a459a6cfSMateusz Guzik
292a459a6cfSMateusz Guzik return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, false));
293a459a6cfSMateusz Guzik out:
294a459a6cfSMateusz Guzik return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, true));
295a459a6cfSMateusz Guzik
296a459a6cfSMateusz Guzik }
297a459a6cfSMateusz Guzik
298a459a6cfSMateusz Guzik int
priv_check_cred_vfs_lookup(struct ucred * cred)299a459a6cfSMateusz Guzik priv_check_cred_vfs_lookup(struct ucred *cred)
300a459a6cfSMateusz Guzik {
301a459a6cfSMateusz Guzik int error;
302a459a6cfSMateusz Guzik
303a459a6cfSMateusz Guzik if (__predict_false(mac_priv_check_fp_flag ||
304a459a6cfSMateusz Guzik mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
305a459a6cfSMateusz Guzik return (priv_check_cred_vfs_lookup_slow(cred));
306a459a6cfSMateusz Guzik
307a459a6cfSMateusz Guzik error = EPERM;
30805e1e482SMariusz Zaborski if (cred->cr_uid == 0 && suser_enabled(cred))
309a459a6cfSMateusz Guzik error = 0;
310a459a6cfSMateusz Guzik return (error);
311a459a6cfSMateusz Guzik }
312a459a6cfSMateusz Guzik
313a459a6cfSMateusz Guzik int
priv_check_cred_vfs_lookup_nomac(struct ucred * cred)314a459a6cfSMateusz Guzik priv_check_cred_vfs_lookup_nomac(struct ucred *cred)
315a459a6cfSMateusz Guzik {
316a459a6cfSMateusz Guzik int error;
317a459a6cfSMateusz Guzik
318a459a6cfSMateusz Guzik if (__predict_false(mac_priv_check_fp_flag ||
319a459a6cfSMateusz Guzik mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
320a459a6cfSMateusz Guzik return (EAGAIN);
321a459a6cfSMateusz Guzik
322a459a6cfSMateusz Guzik error = EPERM;
32305e1e482SMariusz Zaborski if (cred->cr_uid == 0 && suser_enabled(cred))
324a459a6cfSMateusz Guzik error = 0;
325a459a6cfSMateusz Guzik return (error);
326a459a6cfSMateusz Guzik }
327a459a6cfSMateusz Guzik
328a459a6cfSMateusz Guzik static int __noinline
priv_check_cred_vfs_generation_slow(struct ucred * cred)32995275911SMateusz Guzik priv_check_cred_vfs_generation_slow(struct ucred *cred)
3307b2ff0dcSMateusz Guzik {
3317b2ff0dcSMateusz Guzik int error;
3327b2ff0dcSMateusz Guzik
3337b2ff0dcSMateusz Guzik error = priv_check_cred_pre(cred, PRIV_VFS_GENERATION);
3347b2ff0dcSMateusz Guzik if (error)
3357b2ff0dcSMateusz Guzik goto out;
3367b2ff0dcSMateusz Guzik
3377b2ff0dcSMateusz Guzik if (jailed(cred)) {
3387b2ff0dcSMateusz Guzik error = EPERM;
3397b2ff0dcSMateusz Guzik goto out;
3407b2ff0dcSMateusz Guzik }
3417b2ff0dcSMateusz Guzik
34205e1e482SMariusz Zaborski if (cred->cr_uid == 0 && suser_enabled(cred)) {
3437b2ff0dcSMateusz Guzik error = 0;
3447b2ff0dcSMateusz Guzik goto out;
3457b2ff0dcSMateusz Guzik }
3467b2ff0dcSMateusz Guzik
3477b2ff0dcSMateusz Guzik return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, false));
3487b2ff0dcSMateusz Guzik out:
3497b2ff0dcSMateusz Guzik return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, true));
3507b2ff0dcSMateusz Guzik
3517b2ff0dcSMateusz Guzik }
35295275911SMateusz Guzik
35395275911SMateusz Guzik int
priv_check_cred_vfs_generation(struct ucred * cred)35495275911SMateusz Guzik priv_check_cred_vfs_generation(struct ucred *cred)
35595275911SMateusz Guzik {
35695275911SMateusz Guzik int error;
35795275911SMateusz Guzik
35895275911SMateusz Guzik if (__predict_false(mac_priv_check_fp_flag ||
35995275911SMateusz Guzik mac_priv_grant_fp_flag || SDT_PROBES_ENABLED()))
36095275911SMateusz Guzik return (priv_check_cred_vfs_generation_slow(cred));
36195275911SMateusz Guzik
36295275911SMateusz Guzik error = EPERM;
36305e1e482SMariusz Zaborski if (!jailed(cred) && cred->cr_uid == 0 && suser_enabled(cred))
36495275911SMateusz Guzik error = 0;
36595275911SMateusz Guzik return (error);
36695275911SMateusz Guzik }
367