aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Marshall <tdm.code@gmail.com>2017-04-28 22:46:37 +0000
committerAndreas Blaesius <skate4life@gmx.de>2017-06-07 15:23:16 +0000
commit6bd3eaad59ab6a02de1387a341b56bcf49fc1998 (patch)
treed7d103a91ab70451c8bf6f466519848f0efa4595
parent111f43bf5162b3160c2c46fb06651094a3e00f26 (diff)
downloadkernel_samsung_tuna-6bd3eaad59ab6a02de1387a341b56bcf49fc1998.tar.gz
kernel_samsung_tuna-6bd3eaad59ab6a02de1387a341b56bcf49fc1998.tar.bz2
kernel_samsung_tuna-6bd3eaad59ab6a02de1387a341b56bcf49fc1998.zip
kernel: Only expose su when daemon is running
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 Adapted from https://review.lineageos.org/#/c/170648 Used @stargo's PF_SU/PF_FREEZER_NOSIG overlap fix Signed-off-by: D. Andrei Măceș <dmaces@nd.edu>
-rw-r--r--fs/exec.c5
-rw-r--r--fs/namei.c8
-rw-r--r--fs/readdir.c20
-rw-r--r--include/linux/dcache.h7
-rw-r--r--include/linux/sched.h8
-rw-r--r--kernel/exit.c5
-rw-r--r--kernel/fork.c2
-rw-r--r--kernel/sched.c32
8 files changed, 87 insertions, 0 deletions
diff --git a/fs/exec.c b/fs/exec.c
index 4adf6379d0a..16735e8043c 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1527,6 +1527,11 @@ static int do_execve_common(const char *filename,
if (retval < 0)
goto out;
+ if (d_is_su(file->f_dentry) && capable(CAP_SYS_ADMIN)) {
+ 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 c31233018db..c202c8d4c13 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1623,6 +1623,14 @@ static int path_lookupat(int dfd, const char *name,
}
}
+ if (!err) {
+ struct super_block *sb = nd->inode->i_sb;
+ if (sb->s_flags & MS_RDONLY) {
+ 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 356f71528ad..77d68850195 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -47,6 +47,14 @@ out:
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..
*
@@ -68,6 +76,7 @@ struct old_linux_dirent {
struct readdir_callback {
struct old_linux_dirent __user * dirent;
int result;
+ bool romnt;
};
static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
@@ -84,6 +93,8 @@ static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset
buf->result = -EOVERFLOW;
return -EOVERFLOW;
}
+ if (hide_name(name, namlen) && buf->romnt)
+ return 0;
buf->result++;
dirent = buf->dirent;
if (!access_ok(VERIFY_WRITE, dirent,
@@ -116,6 +127,7 @@ SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
buf.result = 0;
buf.dirent = dirent;
+ buf.romnt = (file->f_path.dentry->d_sb->s_flags & MS_RDONLY);
error = vfs_readdir(file, fillonedir, &buf);
if (buf.result)
@@ -144,6 +156,7 @@ struct getdents_callback {
struct linux_dirent __user * previous;
int count;
int error;
+ bool romnt;
};
static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
@@ -163,6 +176,8 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
buf->error = -EOVERFLOW;
return -EOVERFLOW;
}
+ if (hide_name(name, namlen) && buf->romnt)
+ return 0;
dirent = buf->previous;
if (dirent) {
if (__put_user(offset, &dirent->d_off))
@@ -210,6 +225,7 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
buf.previous = NULL;
buf.count = count;
buf.error = 0;
+ buf.romnt = (file->f_path.dentry->d_sb->s_flags & MS_RDONLY);
error = vfs_readdir(file, filldir, &buf);
if (error >= 0)
@@ -231,6 +247,7 @@ struct getdents_callback64 {
struct linux_dirent64 __user * previous;
int count;
int error;
+ bool romnt;
};
static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
@@ -244,6 +261,8 @@ static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
buf->error = -EINVAL; /* only used if we fail.. */
if (reclen > buf->count)
return -EINVAL;
+ if (hide_name(name, namlen) && buf->romnt)
+ return 0;
dirent = buf->previous;
if (dirent) {
if (__put_user(offset, &dirent->d_off))
@@ -293,6 +312,7 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
buf.previous = NULL;
buf.count = count;
buf.error = 0;
+ buf.romnt = (file->f_path.dentry->d_sb->s_flags & MS_RDONLY);
error = vfs_readdir(file, filldir64, &buf);
if (error >= 0)
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 1118fd95c70..e63ee005cd4 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -423,6 +423,13 @@ static inline bool d_mountpoint(struct dentry *dentry)
extern struct dentry *lookup_create(struct nameidata *nd, int is_dir);
+static inline bool d_is_su(const struct dentry *dentry)
+{
+ return dentry &&
+ 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 257b4bdbaae..f9b8a336dd1 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -93,6 +93,12 @@ struct sched_param {
#include <asm/processor.h>
+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;
@@ -1794,6 +1800,8 @@ extern int task_free_unregister(struct notifier_block *n);
#define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezable */
#define PF_FREEZER_NOSIG 0x80000000 /* Freezer won't send signals to it */
+#define PF_SU 0x00001000 /* task is su */
+
/*
* Only the _current_ task can read/write to tsk->flags, but other
* tasks can access tsk->flags in readonly mode for example
diff --git a/kernel/exit.c b/kernel/exit.c
index bfeca60432c..ea9b875239c 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -954,6 +954,11 @@ NORET_TYPE void do_exit(long code)
exit_irq_thread();
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 2483c50a722..e6572d60488 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -291,6 +291,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
if (err)
goto out;
+ tsk->flags &= ~PF_SU;
+
tsk->stack = ti;
err = prop_local_init_single(&tsk->dirties);
diff --git a/kernel/sched.c b/kernel/sched.c
index efb62f04e61..0484805f742 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -84,6 +84,38 @@
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>
+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);
+}
+
/*
* Convert user-nice values [ -20 ... 0 ... 19 ]
* to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ],