From 70cfbfda0071b16160b82835a757ebecd14dc48b Mon Sep 17 00:00:00 2001 From: Tom Marshall Date: Fri, 28 Apr 2017 22:46:37 +0000 Subject: [PATCH] kernel: Only expose su when daemon is running Note: this is for the 3.4 kernel and lacks the read-only mount point logic due to the non-extensible readdir implementation. It has been claimed that the PG implementation of 'su' has security vulnerabilities even when disabled. Unfortunately, the people that find these vulnerabilities often like to keep them private so they can profit from exploits while leaving users exposed to malicious hackers. In order to reduce the attack surface for vulnerabilites, it is therefore necessary to make 'su' completely inaccessible when it is not in use (except by the root and system users). Change-Id: Ia7d50ba46c3d932c2b0ca5fc8e9ec69ec9045f85 --- diff --git a/fs/exec.c b/fs/exec.c index a4d05ce..b8c9af0 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1591,6 +1591,11 @@ if (retval < 0) goto out; + if (capable(CAP_SYS_ADMIN) && d_is_su(file->f_dentry)) { + current->flags |= PF_SU; + su_exec(); + } + /* execve succeeded */ current->fs->in_exec = 0; current->in_execve = 0; diff --git a/fs/namei.c b/fs/namei.c index df12b57..0446469 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1800,6 +1800,11 @@ } } + if (!err) { + if (d_is_su(nd->path.dentry) && !su_visible()) + err = -ENOENT; + } + if (base) fput(base); diff --git a/fs/readdir.c b/fs/readdir.c index cc0a822..106f156 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -47,6 +47,14 @@ EXPORT_SYMBOL(vfs_readdir); +static bool hide_name(const char *name, int namlen) +{ + if (namlen == 2 && !memcmp(name, "su", 2)) + if (!su_visible()) + return true; + return false; +} + /* * Traditional linux readdir() handling.. * @@ -84,6 +92,8 @@ buf->result = -EOVERFLOW; return -EOVERFLOW; } + if (hide_name(name, namlen)) + return 0; buf->result++; dirent = buf->dirent; if (!access_ok(VERIFY_WRITE, dirent, @@ -163,6 +173,8 @@ buf->error = -EOVERFLOW; return -EOVERFLOW; } + if (hide_name(name, namlen)) + return 0; dirent = buf->previous; if (dirent) { if (__put_user(offset, &dirent->d_off)) @@ -244,6 +256,8 @@ buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; + if (hide_name(name, namlen)) + return 0; dirent = buf->previous; if (dirent) { if (__put_user(offset, &dirent->d_off)) diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 92e9d19..13efe38 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -403,6 +403,11 @@ extern void d_clear_need_lookup(struct dentry *dentry); +static inline bool d_is_su(const struct dentry *dentry) +{ + return dentry->d_name.len == 2 && !memcmp(dentry->d_name.name, "su", 2); +} + extern int sysctl_vfs_cache_pressure; #endif /* __LINUX_DCACHE_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 28f14d2..17962ef 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -93,6 +93,12 @@ #include +int su_instances(void); +bool su_running(void); +bool su_visible(void); +void su_exec(void); +void su_exit(void); + struct exec_domain; struct futex_pi_state; struct robust_list_head; @@ -2008,6 +2014,8 @@ TASK_PFA_SET(SPREAD_SLAB, spread_slab) TASK_PFA_CLEAR(SPREAD_SLAB, spread_slab) +#define PF_SU 0x10000000 /* task is su */ + /* * Do not use outside of architecture code which knows its limitations. * diff --git a/kernel/exit.c b/kernel/exit.c index f28427b..3eafd26 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -957,6 +957,11 @@ } exit_signals(tsk); /* sets PF_EXITING */ + + if (tsk->flags & PF_SU) { + su_exit(); + } + /* * tsk->flags are checked in the futex code to protect against * an exiting task cleaning up the robust pi futexes. diff --git a/kernel/fork.c b/kernel/fork.c index 75dc3dd..23695d2 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -295,6 +295,8 @@ if (err) goto out; + tsk->flags &= ~PF_SU; + tsk->stack = ti; #ifdef CONFIG_SECCOMP /* diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5c06094..04fa21e 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -89,6 +89,38 @@ #define CREATE_TRACE_POINTS #include +static atomic_t __su_instances; + +int su_instances(void) +{ + return atomic_read(&__su_instances); +} + +bool su_running(void) +{ + return su_instances() > 0; +} + +bool su_visible(void) +{ + uid_t uid = current_uid(); + if (su_running()) + return true; + if (uid == 0 || uid == 1000) + return true; + return false; +} + +void su_exec(void) +{ + atomic_inc(&__su_instances); +} + +void su_exit(void) +{ + atomic_dec(&__su_instances); +} + ATOMIC_NOTIFIER_HEAD(migration_notifier_head); void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period)