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