Skip to content

Commit bca382c

Browse files
committed
Implement Trusted Path Execution
This is modified from Brad Spengler/PaX Team's code in the last public patch of grsecurity/PaX based on my understanding of the code. Changes or omissions from the original code are mine and don't reflect the original grsecurity/PaX code. Trusted Path Execution (TPE) will restrict certain users so they are only able to execute files in root-owned directories writable only by root. This makes it far harder for an attacker to execute their own code.
1 parent 9c8dad7 commit bca382c

File tree

7 files changed

+249
-1
lines changed

7 files changed

+249
-1
lines changed

Documentation/admin-guide/sysctl/fs.rst

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ Currently, these files are in /proc/sys/fs:
4848
- suid_dumpable
4949
- super-max
5050
- super-nr
51+
- tpe
52+
- tpe_restrict_all
53+
- tpe_invert
54+
- tpe_gid
5155

5256

5357
aio-nr & aio-max-nr
@@ -326,6 +330,60 @@ This denotes the maximum number of mounts that may exist
326330
in a mount namespace.
327331

328332

333+
tpe
334+
---
335+
336+
This indicates whether Trusted Path Execution (TPE) is
337+
enabled.
338+
339+
When tpe is set to (0), TPE is disabled. When tpe is set
340+
to (1), you will be able to choose a gid to add to the
341+
supplementary groups of users you want to mark as "untrusted."
342+
These users will not be able to execute any files that are not in
343+
root-owned directories writable only by root. This makes it far
344+
harder for attackers to execute their own code.
345+
346+
The kernel config option CONFIG_SECURITY_TPE sets the
347+
default value of tpe.
348+
349+
350+
tpe_restrict_all
351+
----------------
352+
353+
If tpe_restrict_all is enabled, all non-root users will be covered under
354+
a weaker TPE restriction. This is separate from, and in addition to,
355+
the main TPE options that you have selected elsewhere. Thus, if a
356+
"trusted" GID is chosen, this restriction applies to even that GID.
357+
Under this restriction, all non-root users will only be allowed to
358+
execute files in directories they own that are not group or
359+
world-writable, or in directories owned by root and writable only by
360+
root.
361+
362+
The kernel config option CONFIG_SECURITY_TPE_ALL sets the
363+
default value of tpe_restrict_all.
364+
365+
366+
tpe_invert
367+
----------
368+
369+
If tpe_invert is enabled, the group you specify in the TPE configuration will
370+
decide what group TPE restrictions will be *disabled* for. This
371+
option is useful if you want TPE restrictions to be applied to most
372+
users on the system.
373+
374+
The kernel config option CONFIG_SECURITY_TPE_INVERT sets the
375+
default value of tpe_invert.
376+
377+
378+
tpe_gid
379+
-------
380+
381+
Setting this GID determines what group TPE restrictions will be
382+
enabled or disabled for.
383+
384+
The kernel config option CONFIG_SECURITY_TPE_GID sets the
385+
default value of tpe_gid.
386+
329387

330388
2. /proc/sys/fs/binfmt_misc
331389
===========================

fs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ obj-y := open.o read_write.o file_table.o super.o \
1313
seq_file.o xattr.o libfs.o fs-writeback.o \
1414
pnode.o splice.o sync.o utimes.o d_path.o \
1515
stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
16-
fs_types.o fs_context.o fs_parser.o fsopen.o
16+
fs_types.o fs_context.o fs_parser.o fsopen.o tpe.o
1717

1818
ifeq ($(CONFIG_BLOCK),y)
1919
obj-y += buffer.o block_dev.o direct-io.o mpage.o

fs/exec.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,6 +1866,11 @@ static int __do_execve_file(int fd, struct filename *filename,
18661866
if (retval < 0)
18671867
goto out;
18681868

1869+
if (!tpe_allow(file)) {
1870+
retval = -EACCES;
1871+
goto out;
1872+
}
1873+
18691874
retval = copy_strings_kernel(1, &bprm->filename, bprm);
18701875
if (retval < 0)
18711876
goto out;

fs/tpe.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/kernel.h>
3+
#include <linux/sched.h>
4+
#include <linux/file.h>
5+
#include <linux/fs.h>
6+
#include <linux/cred.h>
7+
#include <linux/printk.h>
8+
9+
#define TPE_GLOBAL_UID(x) from_kuid_munged(&init_user_ns, (x))
10+
#define TPE_GLOBAL_GID(x) from_kgid_munged(&init_user_ns, (x))
11+
#define tpe_is_global_root(x) uid_eq((x), GLOBAL_ROOT_UID)
12+
#define tpe_is_global_nonroot(x) (!uid_eq((x), GLOBAL_ROOT_UID))
13+
#define tpe_is_global_nonroot_gid(x) (!gid_eq((x), GLOBAL_ROOT_GID))
14+
15+
int security_tpe = IS_ENABLED(CONFIG_SECURITY_TPE);
16+
int security_tpe_all = IS_ENABLED(CONFIG_SECURITY_TPE_ALL);
17+
int security_tpe_invert = IS_ENABLED(CONFIG_SECURITY_TPE_INVERT);
18+
kgid_t security_tpe_gid = KGIDT_INIT(CONFIG_SECURITY_TPE_GID);
19+
20+
int
21+
tpe_allow(const struct file *file)
22+
{
23+
struct inode *inode = d_backing_inode(file->f_path.dentry->d_parent);
24+
struct inode *file_inode = d_backing_inode(file->f_path.dentry);
25+
const struct cred *cred = current_cred();
26+
char *msg = NULL;
27+
char *msg2 = NULL;
28+
29+
if (!security_tpe)
30+
return 1;
31+
32+
// never restrict root
33+
if (tpe_is_global_root(cred->uid))
34+
return 1;
35+
36+
if (security_tpe_all) {
37+
if (tpe_is_global_nonroot(inode->i_uid) && !uid_eq(inode->i_uid, cred->uid))
38+
msg = "directory not owned by user";
39+
else if (inode->i_mode & S_IWOTH)
40+
msg = "file in world-writable directory";
41+
else if ((inode->i_mode & S_IWGRP) && tpe_is_global_nonroot_gid(inode->i_gid))
42+
msg = "file in group-writable directory";
43+
else if (file_inode->i_mode & S_IWOTH)
44+
msg = "file is world-writable";
45+
} else {
46+
if (security_tpe_invert && !in_group_p(security_tpe_gid))
47+
msg2 = "not being in trusted group";
48+
else if (!security_tpe_invert && in_group_p(security_tpe_gid))
49+
msg2 = "being in untrusted group";
50+
else
51+
return 1;
52+
53+
if (tpe_is_global_nonroot(inode->i_uid))
54+
msg = "file in non-root-owned directory";
55+
else if (inode->i_mode & S_IWOTH)
56+
msg = "file in world-writable directory";
57+
else if ((inode->i_mode & S_IWGRP) && tpe_is_global_nonroot_gid(inode->i_gid))
58+
msg = "file in group-writable directory";
59+
else if (file_inode->i_mode & S_IWOTH)
60+
msg = "file is world-writable";
61+
}
62+
63+
if (msg) {
64+
char fullmsg[70] = {0};
65+
66+
if (msg2)
67+
snprintf(fullmsg, sizeof(fullmsg)-1, "%s and %s", msg, msg2);
68+
else
69+
snprintf(fullmsg, sizeof(fullmsg)-1, "%s", msg);
70+
71+
pr_warn_ratelimited("TPE: denied attempt to execute file Reason: %s\n", fullmsg);
72+
return 0;
73+
}
74+
return 1;
75+
}

include/linux/fs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ extern int sysctl_protected_hardlinks;
8484
extern int sysctl_protected_fifos;
8585
extern int sysctl_protected_regular;
8686

87+
extern int tpe_allow(const struct file *file);
88+
extern int security_tpe;
89+
extern int security_tpe_all;
90+
extern int security_tpe_invert;
91+
extern kgid_t security_tpe_gid;
92+
8793
typedef __kernel_rwf_t rwf_t;
8894

8995
struct buffer_head;

kernel/sysctl.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,6 +1979,41 @@ static struct ctl_table fs_table[] = {
19791979
.proc_handler = proc_dointvec_minmax,
19801980
.extra1 = SYSCTL_ONE,
19811981
},
1982+
{
1983+
.procname = "tpe",
1984+
.data = &security_tpe,
1985+
.maxlen = sizeof(int),
1986+
.mode = 0644,
1987+
.proc_handler = proc_dointvec,
1988+
.extra1 = SYSCTL_ZERO,
1989+
.extra2 = SYSCTL_ONE,
1990+
},
1991+
{
1992+
.procname = "tpe_restrict_all",
1993+
.data = &security_tpe_all,
1994+
.maxlen = sizeof(int),
1995+
.mode = 0644,
1996+
.proc_handler = proc_dointvec,
1997+
.extra1 = SYSCTL_ZERO,
1998+
.extra2 = SYSCTL_ONE,
1999+
},
2000+
{
2001+
.procname = "tpe_invert",
2002+
.data = &security_tpe_invert,
2003+
.maxlen = sizeof(int),
2004+
.mode = 0644,
2005+
.proc_handler = proc_dointvec,
2006+
.extra1 = SYSCTL_ZERO,
2007+
.extra2 = SYSCTL_ONE,
2008+
},
2009+
{
2010+
.procname = "tpe_gid",
2011+
.data = &security_tpe_gid,
2012+
.maxlen = sizeof(int),
2013+
.mode = 0644,
2014+
.proc_handler = proc_dointvec,
2015+
.extra1 = SYSCTL_ZERO,
2016+
},
19822017
{ }
19832018
};
19842019

security/Kconfig

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,75 @@ config SECURITY_TIOCSTI_RESTRICT
4242

4343
If you are unsure how to answer this question, answer N.
4444

45+
config SECURITY_TPE
46+
bool "Trusted Path Execution (TPE)"
47+
default n
48+
help
49+
If you say Y here, you will be able to choose a gid to add to the
50+
supplementary groups of users you want to mark as "untrusted."
51+
These users will not be able to execute any files that are not in
52+
root-owned directories writable only by root.
53+
54+
This setting can be overridden at runtime via the fs.enable_tpe
55+
sysctl.
56+
57+
If unsure, say N.
58+
59+
config SECURITY_TPE_ALL
60+
bool "Partially restrict all non-root users"
61+
depends on SECURITY_TPE
62+
help
63+
If you say Y here, all non-root users will be covered under
64+
a weaker TPE restriction. This is separate from, and in addition to,
65+
the main TPE options that you have selected elsewhere. Thus, if a
66+
"trusted" GID is chosen, this restriction applies to even that GID.
67+
Under this restriction, all non-root users will only be allowed to
68+
execute files in directories they own that are not group or
69+
world-writable, or in directories owned by root and writable only by
70+
root.
71+
72+
This setting can be overridden at runtime via the fs.tpe_restrict_all
73+
sysctl.
74+
75+
config SECURITY_TPE_INVERT
76+
bool "Invert GID option"
77+
depends on SECURITY_TPE
78+
help
79+
If you say Y here, the group you specify in the TPE configuration will
80+
decide what group TPE restrictions will be *disabled* for. This
81+
option is useful if you want TPE restrictions to be applied to most
82+
users on the system.
83+
84+
This setting can be overridden at runtime via the fs.tpe_invert
85+
sysctl.
86+
87+
config SECURITY_TPE_GID
88+
int
89+
default SECURITY_TPE_UNTRUSTED_GID if (SECURITY_TPE && !SECURITY_TPE_INVERT)
90+
default SECURITY_TPE_TRUSTED_GID if (SECURITY_TPE && SECURITY_TPE_INVERT)
91+
92+
config SECURITY_TPE_UNTRUSTED_GID
93+
int "GID for TPE-untrusted users"
94+
depends on SECURITY_TPE && !SECURITY_TPE_INVERT
95+
default 1005
96+
help
97+
Setting this GID determines what group TPE restrictions will be
98+
*enabled* for.
99+
100+
This setting can be overridden at runtime via the fs.tpe_gid
101+
sysctl.
102+
103+
config SECURITY_TPE_TRUSTED_GID
104+
int "GID for TPE-trusted users"
105+
depends on SECURITY_TPE && SECURITY_TPE_INVERT
106+
default 1005
107+
help
108+
Setting this GID determines what group TPE restrictions will be
109+
*disabled* for.
110+
111+
This setting can be overridden at runtime via the fs.tpe_gid
112+
sysctl.
113+
45114
config SECURITY
46115
bool "Enable different security models"
47116
depends on SYSFS

0 commit comments

Comments
 (0)