Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/kern_priv.c
/*- | /*- | ||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | ||||
* | * | ||||
* Copyright (c) 2006 nCircle Network Security, Inc. | * Copyright (c) 2006 nCircle Network Security, Inc. | ||||
* Copyright (c) 2009 Robert N. M. Watson | * Copyright (c) 2009 Robert N. M. Watson | ||||
* Copyright (c) 2020 Mariusz Zaborski <oshogbo@FreeBSD.org> | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* This software was developed by Robert N. M. Watson for the TrustedBSD | * This software was developed by Robert N. M. Watson for the TrustedBSD | ||||
* Project under contract to nCircle Network Security, Inc. | * Project under contract to nCircle Network Security, Inc. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
Show All 17 Lines | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/jail.h> | #include <sys/jail.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | |||||
#include <sys/mutex.h> | |||||
#include <sys/sx.h> | |||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/sdt.h> | #include <sys/sdt.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
/* | /* | ||||
* `suser_enabled' (which can be set by the security.bsd.suser_enabled | * `suser_enabled' (which can be set by the security.bsd.suser_enabled | ||||
* sysctl) determines whether the system 'super-user' policy is in effect. If | * sysctl) determines whether the system 'super-user' policy is in effect. If | ||||
* it is nonzero, an effective uid of 0 connotes special privilege, | * it is nonzero, an effective uid of 0 connotes special privilege, | ||||
* overriding many mandatory and discretionary protections. If it is zero, | * overriding many mandatory and discretionary protections. If it is zero, | ||||
* uid 0 is offered no special privilege in the kernel security policy. | * uid 0 is offered no special privilege in the kernel security policy. | ||||
* Setting it to zero may seriously impact the functionality of many existing | * Setting it to zero may seriously impact the functionality of many existing | ||||
* userland programs, and should not be done without careful consideration of | * userland programs, and should not be done without careful consideration of | ||||
* the consequences. | * the consequences. | ||||
*/ | */ | ||||
static int __read_mostly suser_enabled = 1; | |||||
SYSCTL_INT(_security_bsd, OID_AUTO, suser_enabled, CTLFLAG_RWTUN, | |||||
&suser_enabled, 0, "processes with uid 0 have privilege"); | |||||
static bool | |||||
suser_enabled(struct ucred *cred) | |||||
{ | |||||
return (prison_allow(cred, PR_ALLOW_SUSER) ? true : false); | |||||
} | |||||
static void inline | |||||
prison_suser_set(struct prison *pr, int enabled) | |||||
{ | |||||
if (enabled) { | |||||
pr->pr_allow |= PR_ALLOW_SUSER; | |||||
} else { | |||||
pr->pr_allow &= ~PR_ALLOW_SUSER; | |||||
} | |||||
} | |||||
static int | |||||
sysctl_kern_suser_enabled(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct prison *pr, *cpr; | |||||
struct ucred *cred; | |||||
int descend, error, enabled; | |||||
cred = req->td->td_ucred; | |||||
enabled = suser_enabled(cred); | |||||
error = sysctl_handle_int(oidp, &enabled, 0, req); | |||||
if (error || !req->newptr) | |||||
return (error); | |||||
pr = cred->cr_prison; | |||||
sx_slock(&allprison_lock); | |||||
mtx_lock(&pr->pr_mtx); | |||||
prison_suser_set(pr, enabled); | |||||
if (!enabled) { | |||||
FOREACH_PRISON_DESCENDANT_LOCKED(pr, cpr, descend) { | |||||
prison_suser_set(cpr, 0); | |||||
} | |||||
} | |||||
mtx_unlock(&pr->pr_mtx); | |||||
sx_sunlock(&allprison_lock); | |||||
return (0); | |||||
} | |||||
SYSCTL_PROC(_security_bsd, OID_AUTO, suser_enabled, CTLTYPE_INT | | |||||
CTLFLAG_RWTUN | CTLFLAG_PRISON, 0, 0, &sysctl_kern_suser_enabled, "I", | |||||
"Processes with uid 0 have privilege"); | |||||
static int unprivileged_mlock = 1; | static int unprivileged_mlock = 1; | ||||
SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_mlock, CTLFLAG_RWTUN, | SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_mlock, CTLFLAG_RWTUN, | ||||
&unprivileged_mlock, 0, "Allow non-root users to call mlock(2)"); | &unprivileged_mlock, 0, "Allow non-root users to call mlock(2)"); | ||||
static int unprivileged_read_msgbuf = 1; | static int unprivileged_read_msgbuf = 1; | ||||
SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf, | SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf, | ||||
CTLFLAG_RW, &unprivileged_read_msgbuf, 0, | CTLFLAG_RW, &unprivileged_read_msgbuf, 0, | ||||
"Unprivileged processes may read the kernel message buffer"); | "Unprivileged processes may read the kernel message buffer"); | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | priv_check_cred(struct ucred *cred, int priv) | ||||
* may grant privilege. For now, we allow short-circuit boolean | * may grant privilege. For now, we allow short-circuit boolean | ||||
* evaluation, so may not call all policies. Perhaps we should. | * evaluation, so may not call all policies. Perhaps we should. | ||||
* | * | ||||
* Superuser policy grants privilege based on the effective (or in | * Superuser policy grants privilege based on the effective (or in | ||||
* the case of specific privileges, real) uid being 0. We allow the | * the case of specific privileges, real) uid being 0. We allow the | ||||
* superuser policy to be globally disabled, although this is | * superuser policy to be globally disabled, although this is | ||||
* currenty of limited utility. | * currenty of limited utility. | ||||
*/ | */ | ||||
if (suser_enabled) { | if (suser_enabled(cred)) { | ||||
switch (priv) { | switch (priv) { | ||||
case PRIV_MAXFILES: | case PRIV_MAXFILES: | ||||
case PRIV_MAXPROC: | case PRIV_MAXPROC: | ||||
case PRIV_PROC_LIMIT: | case PRIV_PROC_LIMIT: | ||||
if (cred->cr_ruid == 0) { | if (cred->cr_ruid == 0) { | ||||
error = 0; | error = 0; | ||||
goto out; | goto out; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
priv_check_cred_vfs_lookup_slow(struct ucred *cred) | priv_check_cred_vfs_lookup_slow(struct ucred *cred) | ||||
{ | { | ||||
int error; | int error; | ||||
error = priv_check_cred_pre(cred, PRIV_VFS_LOOKUP); | error = priv_check_cred_pre(cred, PRIV_VFS_LOOKUP); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
if (cred->cr_uid == 0 && suser_enabled) { | if (cred->cr_uid == 0 && suser_enabled(cred)) { | ||||
error = 0; | error = 0; | ||||
goto out; | goto out; | ||||
} | } | ||||
return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, false)); | return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, false)); | ||||
out: | out: | ||||
return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, true)); | return (priv_check_cred_post(cred, PRIV_VFS_LOOKUP, error, true)); | ||||
} | } | ||||
int | int | ||||
priv_check_cred_vfs_lookup(struct ucred *cred) | priv_check_cred_vfs_lookup(struct ucred *cred) | ||||
{ | { | ||||
int error; | int error; | ||||
if (__predict_false(mac_priv_check_fp_flag || | if (__predict_false(mac_priv_check_fp_flag || | ||||
mac_priv_grant_fp_flag || SDT_PROBES_ENABLED())) | mac_priv_grant_fp_flag || SDT_PROBES_ENABLED())) | ||||
return (priv_check_cred_vfs_lookup_slow(cred)); | return (priv_check_cred_vfs_lookup_slow(cred)); | ||||
error = EPERM; | error = EPERM; | ||||
if (cred->cr_uid == 0 && suser_enabled) | if (cred->cr_uid == 0 && suser_enabled(cred)) | ||||
error = 0; | error = 0; | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
priv_check_cred_vfs_lookup_nomac(struct ucred *cred) | priv_check_cred_vfs_lookup_nomac(struct ucred *cred) | ||||
{ | { | ||||
int error; | int error; | ||||
if (__predict_false(mac_priv_check_fp_flag || | if (__predict_false(mac_priv_check_fp_flag || | ||||
mac_priv_grant_fp_flag || SDT_PROBES_ENABLED())) | mac_priv_grant_fp_flag || SDT_PROBES_ENABLED())) | ||||
return (EAGAIN); | return (EAGAIN); | ||||
error = EPERM; | error = EPERM; | ||||
if (cred->cr_uid == 0 && suser_enabled) | if (cred->cr_uid == 0 && suser_enabled(cred)) | ||||
error = 0; | error = 0; | ||||
return (error); | return (error); | ||||
} | } | ||||
static int __noinline | static int __noinline | ||||
priv_check_cred_vfs_generation_slow(struct ucred *cred) | priv_check_cred_vfs_generation_slow(struct ucred *cred) | ||||
{ | { | ||||
int error; | int error; | ||||
error = priv_check_cred_pre(cred, PRIV_VFS_GENERATION); | error = priv_check_cred_pre(cred, PRIV_VFS_GENERATION); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
if (jailed(cred)) { | if (jailed(cred)) { | ||||
error = EPERM; | error = EPERM; | ||||
goto out; | goto out; | ||||
} | } | ||||
if (cred->cr_uid == 0 && suser_enabled) { | if (cred->cr_uid == 0 && suser_enabled(cred)) { | ||||
error = 0; | error = 0; | ||||
goto out; | goto out; | ||||
} | } | ||||
return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, false)); | return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, false)); | ||||
out: | out: | ||||
return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, true)); | return (priv_check_cred_post(cred, PRIV_VFS_GENERATION, error, true)); | ||||
} | } | ||||
int | int | ||||
priv_check_cred_vfs_generation(struct ucred *cred) | priv_check_cred_vfs_generation(struct ucred *cred) | ||||
{ | { | ||||
int error; | int error; | ||||
if (__predict_false(mac_priv_check_fp_flag || | if (__predict_false(mac_priv_check_fp_flag || | ||||
mac_priv_grant_fp_flag || SDT_PROBES_ENABLED())) | mac_priv_grant_fp_flag || SDT_PROBES_ENABLED())) | ||||
return (priv_check_cred_vfs_generation_slow(cred)); | return (priv_check_cred_vfs_generation_slow(cred)); | ||||
error = EPERM; | error = EPERM; | ||||
if (!jailed(cred) && cred->cr_uid == 0 && suser_enabled) | if (!jailed(cred) && cred->cr_uid == 0 && suser_enabled(cred)) | ||||
error = 0; | error = 0; | ||||
return (error); | return (error); | ||||
} | } |