1800c9408SRobert Watson /*- 2*4d846d26SWarner 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 34b916b56bSRobert Watson #include <sys/cdefs.h> 35b916b56bSRobert Watson __FBSDID("$FreeBSD$"); 36b916b56bSRobert Watson 37800c9408SRobert Watson #include <sys/param.h> 38800c9408SRobert Watson #include <sys/jail.h> 39800c9408SRobert Watson #include <sys/kernel.h> 4005e1e482SMariusz Zaborski #include <sys/lock.h> 4105e1e482SMariusz Zaborski #include <sys/mutex.h> 4205e1e482SMariusz Zaborski #include <sys/sx.h> 43800c9408SRobert Watson #include <sys/priv.h> 44800c9408SRobert Watson #include <sys/proc.h> 456efcc2f2SRobert Watson #include <sys/sdt.h> 46800c9408SRobert Watson #include <sys/sysctl.h> 47800c9408SRobert Watson #include <sys/systm.h> 48800c9408SRobert Watson 49800c9408SRobert Watson #include <security/mac/mac_framework.h> 50800c9408SRobert Watson 51800c9408SRobert Watson /* 52800c9408SRobert Watson * `suser_enabled' (which can be set by the security.bsd.suser_enabled 53800c9408SRobert Watson * sysctl) determines whether the system 'super-user' policy is in effect. If 54800c9408SRobert Watson * it is nonzero, an effective uid of 0 connotes special privilege, 55800c9408SRobert Watson * overriding many mandatory and discretionary protections. If it is zero, 56800c9408SRobert Watson * uid 0 is offered no special privilege in the kernel security policy. 57800c9408SRobert Watson * Setting it to zero may seriously impact the functionality of many existing 58800c9408SRobert Watson * userland programs, and should not be done without careful consideration of 59800c9408SRobert Watson * the consequences. 60800c9408SRobert Watson */ 6105e1e482SMariusz Zaborski 6205e1e482SMariusz Zaborski static bool 6305e1e482SMariusz Zaborski suser_enabled(struct ucred *cred) 6405e1e482SMariusz Zaborski { 6505e1e482SMariusz Zaborski 660fe74ae6SJamie Gritton return (prison_allow(cred, PR_ALLOW_SUSER)); 6705e1e482SMariusz Zaborski } 6805e1e482SMariusz Zaborski 6905e1e482SMariusz Zaborski static int 7005e1e482SMariusz Zaborski sysctl_kern_suser_enabled(SYSCTL_HANDLER_ARGS) 7105e1e482SMariusz Zaborski { 7205e1e482SMariusz Zaborski struct ucred *cred; 730fe74ae6SJamie Gritton int error, enabled; 7405e1e482SMariusz Zaborski 7505e1e482SMariusz Zaborski cred = req->td->td_ucred; 7605e1e482SMariusz Zaborski enabled = suser_enabled(cred); 7705e1e482SMariusz Zaborski error = sysctl_handle_int(oidp, &enabled, 0, req); 7805e1e482SMariusz Zaborski if (error || !req->newptr) 7905e1e482SMariusz Zaborski return (error); 800fe74ae6SJamie Gritton prison_set_allow(cred, PR_ALLOW_SUSER, enabled); 8105e1e482SMariusz Zaborski return (0); 8205e1e482SMariusz Zaborski } 8305e1e482SMariusz Zaborski 8405e1e482SMariusz Zaborski SYSCTL_PROC(_security_bsd, OID_AUTO, suser_enabled, CTLTYPE_INT | 85f488d5b7SMariusz Zaborski CTLFLAG_RWTUN | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 0, 0, 86f488d5b7SMariusz Zaborski &sysctl_kern_suser_enabled, "I", "Processes with uid 0 have privilege"); 87800c9408SRobert Watson 885eb0d283SAndrey Zonov static int unprivileged_mlock = 1; 89af3b2549SHans Petter Selasky SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_mlock, CTLFLAG_RWTUN, 905eb0d283SAndrey Zonov &unprivileged_mlock, 0, "Allow non-root users to call mlock(2)"); 915eb0d283SAndrey Zonov 92b19d66fdSJamie Gritton static int unprivileged_read_msgbuf = 1; 93b19d66fdSJamie Gritton SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf, 94b19d66fdSJamie Gritton CTLFLAG_RW, &unprivileged_read_msgbuf, 0, 95b19d66fdSJamie Gritton "Unprivileged processes may read the kernel message buffer"); 96b19d66fdSJamie Gritton 976efcc2f2SRobert Watson SDT_PROVIDER_DEFINE(priv); 98d9fae5abSAndriy Gapon SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__ok, "int"); 99d9fae5abSAndriy Gapon SDT_PROBE_DEFINE1(priv, kernel, priv_check, priv__err, "int"); 1006efcc2f2SRobert Watson 1017b2ff0dcSMateusz Guzik static __always_inline int 1027b2ff0dcSMateusz Guzik priv_check_cred_pre(struct ucred *cred, int priv) 1037b2ff0dcSMateusz Guzik { 1047b2ff0dcSMateusz Guzik int error; 1057b2ff0dcSMateusz Guzik 1067b2ff0dcSMateusz Guzik #ifdef MAC 1077b2ff0dcSMateusz Guzik error = mac_priv_check(cred, priv); 1087b2ff0dcSMateusz Guzik #else 1097b2ff0dcSMateusz Guzik error = 0; 1107b2ff0dcSMateusz Guzik #endif 1117b2ff0dcSMateusz Guzik return (error); 1127b2ff0dcSMateusz Guzik } 1137b2ff0dcSMateusz Guzik 1147b2ff0dcSMateusz Guzik static __always_inline int 1157b2ff0dcSMateusz Guzik priv_check_cred_post(struct ucred *cred, int priv, int error, bool handled) 1167b2ff0dcSMateusz Guzik { 1177b2ff0dcSMateusz Guzik 1187b2ff0dcSMateusz Guzik if (__predict_true(handled)) 1197b2ff0dcSMateusz Guzik goto out; 1207b2ff0dcSMateusz Guzik /* 1217b2ff0dcSMateusz Guzik * Now check with MAC, if enabled, to see if a policy module grants 1227b2ff0dcSMateusz Guzik * privilege. 1237b2ff0dcSMateusz Guzik */ 1247b2ff0dcSMateusz Guzik #ifdef MAC 1257b2ff0dcSMateusz Guzik if (mac_priv_grant(cred, priv) == 0) { 1267b2ff0dcSMateusz Guzik error = 0; 1277b2ff0dcSMateusz Guzik goto out; 1287b2ff0dcSMateusz Guzik } 1297b2ff0dcSMateusz Guzik #endif 1307b2ff0dcSMateusz Guzik 1317b2ff0dcSMateusz Guzik /* 1327b2ff0dcSMateusz Guzik * The default is deny, so if no policies have granted it, reject 1337b2ff0dcSMateusz Guzik * with a privilege error here. 1347b2ff0dcSMateusz Guzik */ 1357b2ff0dcSMateusz Guzik error = EPERM; 1367b2ff0dcSMateusz Guzik out: 1377b2ff0dcSMateusz Guzik if (SDT_PROBES_ENABLED()) { 1387b2ff0dcSMateusz Guzik if (error) 1397b2ff0dcSMateusz Guzik SDT_PROBE1(priv, kernel, priv_check, priv__err, priv); 1407b2ff0dcSMateusz Guzik else 1417b2ff0dcSMateusz Guzik SDT_PROBE1(priv, kernel, priv_check, priv__ok, priv); 1427b2ff0dcSMateusz Guzik } 1437b2ff0dcSMateusz Guzik return (error); 1447b2ff0dcSMateusz Guzik } 1457b2ff0dcSMateusz Guzik 146800c9408SRobert Watson /* 147800c9408SRobert Watson * Check a credential for privilege. Lots of good reasons to deny privilege; 148800c9408SRobert Watson * only a few to grant it. 149800c9408SRobert Watson */ 150800c9408SRobert Watson int 151cc426dd3SMateusz Guzik priv_check_cred(struct ucred *cred, int priv) 152800c9408SRobert Watson { 153800c9408SRobert Watson int error; 154800c9408SRobert Watson 155800c9408SRobert Watson KASSERT(PRIV_VALID(priv), ("priv_check_cred: invalid privilege %d", 156800c9408SRobert Watson priv)); 157800c9408SRobert Watson 1587b2ff0dcSMateusz Guzik switch (priv) { 159a459a6cfSMateusz Guzik case PRIV_VFS_LOOKUP: 160a459a6cfSMateusz Guzik return (priv_check_cred_vfs_lookup(cred)); 1617b2ff0dcSMateusz Guzik case PRIV_VFS_GENERATION: 1627b2ff0dcSMateusz Guzik return (priv_check_cred_vfs_generation(cred)); 1637b2ff0dcSMateusz Guzik } 1647b2ff0dcSMateusz Guzik 1657251b786SRobert Watson /* 1667251b786SRobert Watson * We first evaluate policies that may deny the granting of 1677251b786SRobert Watson * privilege unilaterally. 1687251b786SRobert Watson */ 1697b2ff0dcSMateusz Guzik error = priv_check_cred_pre(cred, priv); 170800c9408SRobert Watson if (error) 1716efcc2f2SRobert Watson goto out; 172800c9408SRobert Watson 173800c9408SRobert Watson /* 174800c9408SRobert Watson * Jail policy will restrict certain privileges that may otherwise be 175800c9408SRobert Watson * be granted. 176800c9408SRobert Watson */ 177800c9408SRobert Watson error = prison_priv_check(cred, priv); 178800c9408SRobert Watson if (error) 1796efcc2f2SRobert Watson goto out; 180800c9408SRobert Watson 1815eb0d283SAndrey Zonov if (unprivileged_mlock) { 1825eb0d283SAndrey Zonov /* 1835eb0d283SAndrey Zonov * Allow unprivileged users to call mlock(2)/munlock(2) and 1845eb0d283SAndrey Zonov * mlockall(2)/munlockall(2). 1855eb0d283SAndrey Zonov */ 1865eb0d283SAndrey Zonov switch (priv) { 1875eb0d283SAndrey Zonov case PRIV_VM_MLOCK: 1885eb0d283SAndrey Zonov case PRIV_VM_MUNLOCK: 1895eb0d283SAndrey Zonov error = 0; 1905eb0d283SAndrey Zonov goto out; 1915eb0d283SAndrey Zonov } 1925eb0d283SAndrey Zonov } 1935eb0d283SAndrey Zonov 194b19d66fdSJamie Gritton if (unprivileged_read_msgbuf) { 195b19d66fdSJamie Gritton /* 196b19d66fdSJamie Gritton * Allow an unprivileged user to read the kernel message 197b19d66fdSJamie Gritton * buffer. 198b19d66fdSJamie Gritton */ 199b19d66fdSJamie Gritton if (priv == PRIV_MSGBUF) { 200b19d66fdSJamie Gritton error = 0; 201b19d66fdSJamie Gritton goto out; 202b19d66fdSJamie Gritton } 203b19d66fdSJamie Gritton } 204b19d66fdSJamie Gritton 205800c9408SRobert Watson /* 206800c9408SRobert Watson * Having determined if privilege is restricted by various policies, 2077251b786SRobert Watson * now determine if privilege is granted. At this point, any policy 2087251b786SRobert Watson * may grant privilege. For now, we allow short-circuit boolean 2097251b786SRobert Watson * evaluation, so may not call all policies. Perhaps we should. 210800c9408SRobert Watson * 211800c9408SRobert Watson * Superuser policy grants privilege based on the effective (or in 2127251b786SRobert Watson * the case of specific privileges, real) uid being 0. We allow the 2137251b786SRobert Watson * superuser policy to be globally disabled, although this is 2147251b786SRobert Watson * currenty of limited utility. 215800c9408SRobert Watson */ 21605e1e482SMariusz Zaborski if (suser_enabled(cred)) { 2177251b786SRobert Watson switch (priv) { 2187251b786SRobert Watson case PRIV_MAXFILES: 2197251b786SRobert Watson case PRIV_MAXPROC: 2207251b786SRobert Watson case PRIV_PROC_LIMIT: 2216efcc2f2SRobert Watson if (cred->cr_ruid == 0) { 2226efcc2f2SRobert Watson error = 0; 2236efcc2f2SRobert Watson goto out; 2246efcc2f2SRobert Watson } 2257251b786SRobert Watson break; 22663619b6dSKyle Evans case PRIV_VFS_READ_DIR: 22763619b6dSKyle Evans /* 22863619b6dSKyle Evans * Allow PRIV_VFS_READ_DIR for root if we're not in a 22963619b6dSKyle Evans * jail, otherwise deny unless a MAC policy grants it. 23063619b6dSKyle Evans */ 23163619b6dSKyle Evans if (jailed(cred)) 23263619b6dSKyle Evans break; 23363619b6dSKyle Evans /* FALLTHROUGH */ 2347251b786SRobert Watson default: 2356efcc2f2SRobert Watson if (cred->cr_uid == 0) { 2366efcc2f2SRobert Watson error = 0; 2376efcc2f2SRobert Watson goto out; 2386efcc2f2SRobert Watson } 2397251b786SRobert Watson break; 240800c9408SRobert Watson } 241800c9408SRobert Watson } 242800c9408SRobert Watson 243800c9408SRobert Watson /* 2441e7df843SJamie Gritton * Writes to kernel/physical memory are a typical root-only operation, 2451e7df843SJamie Gritton * but non-root users are expected to be able to read it (provided they 2461e7df843SJamie Gritton * have permission to access /dev/[k]mem). 247c71e3362SJamie Gritton */ 248c71e3362SJamie Gritton if (priv == PRIV_KMEM_READ) { 249c71e3362SJamie Gritton error = 0; 250c71e3362SJamie Gritton goto out; 251c71e3362SJamie Gritton } 252c71e3362SJamie Gritton 253c71e3362SJamie Gritton /* 254b3079544SJamie Gritton * Allow unprivileged process debugging on a per-jail basis. 255b3079544SJamie Gritton * Do this here instead of prison_priv_check(), so it can also 256b3079544SJamie Gritton * apply to prison0. 257b3079544SJamie Gritton */ 258b3079544SJamie Gritton if (priv == PRIV_DEBUG_UNPRIV) { 259b3079544SJamie Gritton if (prison_allow(cred, PR_ALLOW_UNPRIV_DEBUG)) { 260b3079544SJamie Gritton error = 0; 261b3079544SJamie Gritton goto out; 262b3079544SJamie Gritton } 263b3079544SJamie Gritton } 264b3079544SJamie Gritton 2657b2ff0dcSMateusz Guzik return (priv_check_cred_post(cred, priv, error, false)); 2666efcc2f2SRobert Watson out: 2677b2ff0dcSMateusz Guzik return (priv_check_cred_post(cred, priv, error, true)); 268800c9408SRobert Watson } 269800c9408SRobert Watson 270800c9408SRobert Watson int 271800c9408SRobert Watson priv_check(struct thread *td, int priv) 272800c9408SRobert Watson { 273800c9408SRobert Watson 274800c9408SRobert Watson KASSERT(td == curthread, ("priv_check: td != curthread")); 275800c9408SRobert Watson 276cc426dd3SMateusz Guzik return (priv_check_cred(td->td_ucred, priv)); 277800c9408SRobert Watson } 2787b2ff0dcSMateusz Guzik 27995275911SMateusz Guzik static int __noinline 280a459a6cfSMateusz Guzik priv_check_cred_vfs_lookup_slow(struct ucred *cred) 281a459a6cfSMateusz Guzik { 282a459a6cfSMateusz Guzik int error; 283a459a6cfSMateusz Guzik 284a459a6cfSMateusz Guzik error = priv_check_cred_pre(cred, PRIV_VFS_LOOKUP); 285a459a6cfSMateusz Guzik if (error) 286a459a6cfSMateusz Guzik goto out; 287a459a6cfSMateusz Guzik 28805e1e482SMariusz Zaborski if (cred->cr_uid == 0 && suser_enabled(cred)) { 289a459a6cfSMateusz Guzik error = 0; 290a459a6cfSMateusz Guzik goto out; 291a459a6cfSMateusz Guzik } 292a459a6cfSMateusz Guzik 293a459a6cfSMateusz Guzik return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, false)); 294a459a6cfSMateusz Guzik out: 295a459a6cfSMateusz Guzik return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, true)); 296a459a6cfSMateusz Guzik 297a459a6cfSMateusz Guzik } 298a459a6cfSMateusz Guzik 299a459a6cfSMateusz Guzik int 300a459a6cfSMateusz Guzik priv_check_cred_vfs_lookup(struct ucred *cred) 301a459a6cfSMateusz Guzik { 302a459a6cfSMateusz Guzik int error; 303a459a6cfSMateusz Guzik 304a459a6cfSMateusz Guzik if (__predict_false(mac_priv_check_fp_flag || 305a459a6cfSMateusz Guzik mac_priv_grant_fp_flag || SDT_PROBES_ENABLED())) 306a459a6cfSMateusz Guzik return (priv_check_cred_vfs_lookup_slow(cred)); 307a459a6cfSMateusz Guzik 308a459a6cfSMateusz Guzik error = EPERM; 30905e1e482SMariusz Zaborski if (cred->cr_uid == 0 && suser_enabled(cred)) 310a459a6cfSMateusz Guzik error = 0; 311a459a6cfSMateusz Guzik return (error); 312a459a6cfSMateusz Guzik } 313a459a6cfSMateusz Guzik 314a459a6cfSMateusz Guzik int 315a459a6cfSMateusz Guzik priv_check_cred_vfs_lookup_nomac(struct ucred *cred) 316a459a6cfSMateusz Guzik { 317a459a6cfSMateusz Guzik int error; 318a459a6cfSMateusz Guzik 319a459a6cfSMateusz Guzik if (__predict_false(mac_priv_check_fp_flag || 320a459a6cfSMateusz Guzik mac_priv_grant_fp_flag || SDT_PROBES_ENABLED())) 321a459a6cfSMateusz Guzik return (EAGAIN); 322a459a6cfSMateusz Guzik 323a459a6cfSMateusz Guzik error = EPERM; 32405e1e482SMariusz Zaborski if (cred->cr_uid == 0 && suser_enabled(cred)) 325a459a6cfSMateusz Guzik error = 0; 326a459a6cfSMateusz Guzik return (error); 327a459a6cfSMateusz Guzik } 328a459a6cfSMateusz Guzik 329a459a6cfSMateusz Guzik static int __noinline 33095275911SMateusz Guzik priv_check_cred_vfs_generation_slow(struct ucred *cred) 3317b2ff0dcSMateusz Guzik { 3327b2ff0dcSMateusz Guzik int error; 3337b2ff0dcSMateusz Guzik 3347b2ff0dcSMateusz Guzik error = priv_check_cred_pre(cred, PRIV_VFS_GENERATION); 3357b2ff0dcSMateusz Guzik if (error) 3367b2ff0dcSMateusz Guzik goto out; 3377b2ff0dcSMateusz Guzik 3387b2ff0dcSMateusz Guzik if (jailed(cred)) { 3397b2ff0dcSMateusz Guzik error = EPERM; 3407b2ff0dcSMateusz Guzik goto out; 3417b2ff0dcSMateusz Guzik } 3427b2ff0dcSMateusz Guzik 34305e1e482SMariusz Zaborski if (cred->cr_uid == 0 && suser_enabled(cred)) { 3447b2ff0dcSMateusz Guzik error = 0; 3457b2ff0dcSMateusz Guzik goto out; 3467b2ff0dcSMateusz Guzik } 3477b2ff0dcSMateusz Guzik 3487b2ff0dcSMateusz Guzik return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, false)); 3497b2ff0dcSMateusz Guzik out: 3507b2ff0dcSMateusz Guzik return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, true)); 3517b2ff0dcSMateusz Guzik 3527b2ff0dcSMateusz Guzik } 35395275911SMateusz Guzik 35495275911SMateusz Guzik int 35595275911SMateusz Guzik priv_check_cred_vfs_generation(struct ucred *cred) 35695275911SMateusz Guzik { 35795275911SMateusz Guzik int error; 35895275911SMateusz Guzik 35995275911SMateusz Guzik if (__predict_false(mac_priv_check_fp_flag || 36095275911SMateusz Guzik mac_priv_grant_fp_flag || SDT_PROBES_ENABLED())) 36195275911SMateusz Guzik return (priv_check_cred_vfs_generation_slow(cred)); 36295275911SMateusz Guzik 36395275911SMateusz Guzik error = EPERM; 36405e1e482SMariusz Zaborski if (!jailed(cred) && cred->cr_uid == 0 && suser_enabled(cred)) 36595275911SMateusz Guzik error = 0; 36695275911SMateusz Guzik return (error); 36795275911SMateusz Guzik } 368