Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Trusted Path Execution #24

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,11 @@ static int do_execveat_common(int fd, struct filename *filename,
if (retval < 0)
goto out;

if (!tpe_allow(file)) {
retval = -EACCES;
goto out;
}

retval = copy_strings_kernel(1, &bprm->filename, bprm);
if (retval < 0)
goto out;
Expand Down
81 changes: 81 additions & 0 deletions fs/tpe.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* linux/fs/tpe.c
*
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/uidgid.h>
#include <linux/tpe.h>
#include <linux/ratelimit.h>

#define TPE_GLOBAL_UID(x) from_kuid_munged(&init_user_ns, (x))
#define TPE_GLOBAL_GID(x) from_kgid_munged(&init_user_ns, (x))
#define tpe_is_global_root(x) uid_eq((x), GLOBAL_ROOT_UID)
#define tpe_is_global_nonroot(x) (!uid_eq((x), GLOBAL_ROOT_UID))
#define tpe_is_global_nonroot_gid(x) (!gid_eq((x), GLOBAL_ROOT_GID))

int security_tpe = IS_ENABLED(CONFIG_SECURITY_TPE);
kgid_t security_tpe_gid = KGIDT_INIT(CONFIG_SECURITY_TPE_GID);
int security_tpe_all = IS_ENABLED(CONFIG_SECURITY_TPE_ALL);
int security_tpe_invert = IS_ENABLED(CONFIG_SECURITY_TPE_INVERT);

int tpe_allow(const struct file *file)
{
struct inode *inode = d_backing_inode(file->f_path.dentry->d_parent);
struct inode *file_inode = d_backing_inode(file->f_path.dentry);
const struct cred *cred = current_cred();
char *msg = NULL;
char *msg2 = NULL;

// never restrict root
if (tpe_is_global_root(cred->uid))
return 1;

if (security_tpe) {
if (security_tpe_invert && !in_group_p(security_tpe_gid))
msg = "not being in trusted group";
else if (!security_tpe_invert && in_group_p(security_tpe_gid))
msg = "being in untrusted group";
}

// not in any affected group/role
if (!msg)
goto next_check;

if (tpe_is_global_nonroot(inode->i_uid))
msg2 = "file in non-root-owned directory";
else if (inode->i_mode & S_IWOTH)
msg2 = "file in world-writable directory";
else if ((inode->i_mode & S_IWGRP) && tpe_is_global_nonroot_gid(inode->i_gid))
msg2 = "file in group-writable directory";
else if (file_inode->i_mode & S_IWOTH)
msg2 = "file is world-writable";

if (msg && msg2) {
char fullmsg[70] = {0};
snprintf(fullmsg, sizeof(fullmsg)-1, "%s and %s", msg, msg2);
pr_warn_ratelimited("TPE: %s\n",fullmsg);
return 0;
}
msg = NULL;
next_check:
if (!security_tpe || !security_tpe_all)
return 1;

if (tpe_is_global_nonroot(inode->i_uid) && !uid_eq(inode->i_uid, cred->uid))
msg = "directory not owned by user";
else if (inode->i_mode & S_IWOTH)
msg = "file in world-writable directory";
else if ((inode->i_mode & S_IWGRP) && tpe_is_global_nonroot_gid(inode->i_gid))
msg = "file in group-writable directory";
else if (file_inode->i_mode & S_IWOTH)
msg = "file is world-writable";

if (msg) {
pr_warn_ratelimited("TPE: %s\n",msg);
return 0;
}
return 1;
}
10 changes: 10 additions & 0 deletions include/linux/tpe.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* linux/fs/tpe.h
*
*/

extern int security_tpe;
extern kgid_t security_tpe_gid;
extern int security_tpe_all;
extern int security_tpe_invert;
extern int tpe_allow(const struct file *file);
34 changes: 34 additions & 0 deletions kernel/sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,40 @@ static struct ctl_table fs_table[] = {
.proc_handler = proc_dointvec_minmax,
.extra1 = &one,
},
#ifdef CONFIG_SECURITY_TPE
{
.procname = "tpe",
.data = &security_tpe,
.maxlen = sizeof(int),
.mode = 0600,
.proc_handler = &proc_dointvec,
},
{
.procname = "tpe_gid",
.data = &security_tpe_gid,
.maxlen = sizeof(int),
.mode = 0600,
.proc_handler = &proc_dointvec,
},
#endif
#ifdef CONFIG_SECURITY_TPE_INVERT
{
.procname = "tpe_invert",
.data = &security_tpe_invert,
.maxlen = sizeof(int),
.mode = 0600,
.proc_handler = &proc_dointvec,
},
#endif
#ifdef CONFIG_SECURITY_TPE_ALL
{
.procname = "tpe_restrict_all",
.data = &security_enable_tpe_all,
.maxlen = sizeof(int),
.mode = 0600,
.proc_handler = &proc_dointvec,
},
#endif
{ }
};

Expand Down
58 changes: 58 additions & 0 deletions security/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,64 @@ config STATIC_USERMODEHELPER_PATH
If you wish for all usermode helper programs to be disabled,
specify an empty string here (i.e. "").

config SECURITY_TPE
bool "Trusted Path Execution (TPE)"
default n
help
If you say Y here, you will be able to choose a gid to add to the
supplementary groups of users you want to mark as "untrusted."
These users will not be able to execute any files that are not in
root-owned directories writable only by root. If the sysctl option
is enabled, a sysctl option with name "tpe" is created.

config SECURITY_TPE_ALL
bool "Partially restrict all non-root users"
depends on SECURITY_TPE
help
If you say Y here, all non-root users will be covered under
a weaker TPE restriction. This is separate from, and in addition to,
the main TPE options that you have selected elsewhere. Thus, if a
"trusted" GID is chosen, this restriction applies to even that GID.
Under this restriction, all non-root users will only be allowed to
execute files in directories they own that are not group or
world-writable, or in directories owned by root and writable only by
root. If the sysctl option is enabled, a sysctl option with name
"tpe_restrict_all" is created.

config SECURITY_TPE_INVERT
bool "Invert GID option"
depends on SECURITY_TPE
help
If you say Y here, the group you specify in the TPE configuration will
decide what group TPE restrictions will be *disabled* for. This
option is useful if you want TPE restrictions to be applied to most
users on the system. If the sysctl option is enabled, a sysctl option
with name "tpe_invert" is created. Unlike other sysctl options, this
entry will default to on for backward-compatibility.

config SECURITY_TPE_GID
int
default SECURITY_TPE_UNTRUSTED_GID if (SECURITY_TPE && !SECURITY_TPE_INVERT)
default SECURITY_TPE_TRUSTED_GID if (SECURITY_TPE && SECURITY_TPE_INVERT)

config SECURITY_TPE_UNTRUSTED_GID
int "GID for TPE-untrusted users"
depends on SECURITY_TPE && !SECURITY_TPE_INVERT
default 1005
help
Setting this GID determines what group TPE restrictions will be
*enabled* for. If the sysctl option is enabled, a sysctl option
with name "tpe_gid" is created.

config SECURITY_TPE_TRUSTED_GID
int "GID for TPE-trusted users"
depends on SECURITY_TPE && SECURITY_TPE_INVERT
default 1005
help
Setting this GID determines what group TPE restrictions will be
*disabled* for. If the sysctl option is enabled, a sysctl option
with name "tpe_gid" is created.

source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
Expand Down