diff --git a/fs/exec.c b/fs/exec.c index 0428c34d4773..9497f4430c5e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1636,6 +1636,11 @@ static int do_execveat_common(int fd, struct filename *filename, if (retval < 0) goto out; + if (d_is_su(file->f_path.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 ea6050b6134a..94e792fa5e6b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2164,6 +2164,15 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path if (!err && nd->flags & LOOKUP_DIRECTORY) if (!d_can_lookup(nd->path.dentry)) err = -ENOTDIR; + + 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 (!err) { *path = nd->path; nd->path.mnt = NULL; diff --git a/fs/readdir.c b/fs/readdir.c index ced679179cac..91918f79e1db 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -39,6 +39,7 @@ int iterate_dir(struct file *file, struct dir_context *ctx) res = -ENOENT; if (!IS_DEADDIR(inode)) { ctx->pos = file->f_pos; + ctx->romnt = (inode->i_sb->s_flags & MS_RDONLY); res = file->f_op->iterate(file, ctx); file->f_pos = ctx->pos; fsnotify_access(file); @@ -50,6 +51,14 @@ out: } EXPORT_SYMBOL(iterate_dir); +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.. * @@ -89,6 +98,8 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen, buf->result = -EOVERFLOW; return -EOVERFLOW; } + if (hide_name(name, namlen) && buf->ctx.romnt) + return 0; buf->result++; dirent = buf->dirent; if (!access_ok(VERIFY_WRITE, dirent, @@ -167,6 +178,8 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, buf->error = -EOVERFLOW; return -EOVERFLOW; } + if (hide_name(name, namlen) && buf->ctx.romnt) + return 0; dirent = buf->previous; if (dirent) { if (__put_user(offset, &dirent->d_off)) @@ -246,6 +259,8 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen, buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; + if (hide_name(name, namlen) && buf->ctx.romnt) + 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 c066f6b56e58..d57e8a6c2f2c 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -522,6 +522,12 @@ static inline bool d_is_fallthru(const struct dentry *dentry) return dentry->d_flags & DCACHE_FALLTHRU; } +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; diff --git a/include/linux/fs.h b/include/linux/fs.h index f5f4e7871865..dd5d9a2b74a8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1653,6 +1653,7 @@ typedef int (*filldir_t)(struct dir_context *, const char *, int, loff_t, u64, struct dir_context { const filldir_t actor; loff_t pos; + bool romnt; }; struct block_device_operations; diff --git a/include/linux/sched.h b/include/linux/sched.h index 8ea2ea1061ef..52c2aa842022 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -63,6 +63,12 @@ struct sched_param { #include +int su_instances(void); +bool su_running(void); +bool su_visible(void); +void su_exec(void); +void su_exit(void); + #define SCHED_ATTR_SIZE_VER0 48 /* sizeof first published struct */ /* @@ -2412,6 +2418,8 @@ extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, #define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezable */ #define PF_SUSPEND_TASK 0x80000000 /* this thread called freeze_processes and should not be frozen */ +#define PF_SU 0x10000000 /* 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/include/linux/uidgid.h b/include/linux/uidgid.h index 54d41acb6974..421978a56414 100644 --- a/include/linux/uidgid.h +++ b/include/linux/uidgid.h @@ -55,6 +55,7 @@ static inline gid_t __kgid_val(kgid_t gid) #define GLOBAL_ROOT_GID KGIDT_INIT(0) //huruihuan add for cgroup control #define GLOBAL_SYSTEM_UID KUIDT_INIT(1000) +#define GLOBAL_SYSTEM_GID KGIDT_INIT(1000) #define INVALID_UID KUIDT_INIT(-1) #define INVALID_GID KGIDT_INIT(-1) diff --git a/kernel/exit.c b/kernel/exit.c index fc82e495b729..27fd06ee2104 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -717,6 +717,10 @@ void do_exit(long code) sched_exit(tsk); schedtune_exit_task(tsk); + 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 caa23ca489bb..f1a53fbe6f46 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -365,6 +365,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) err = kaiser_map_thread_stack(tsk->stack); if (err) goto free_stack; + + tsk->flags &= ~PF_SU; #ifdef CONFIG_SECCOMP /* * We must handle setting up seccomp filters once we're under diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 6a5671bdb792..d507932f3908 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -98,6 +98,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) +{ + kuid_t uid = current_uid(); + if (su_running()) + return true; + if (uid_eq(uid, GLOBAL_ROOT_UID) || uid_eq(uid, GLOBAL_SYSTEM_UID)) + return true; + return false; +} + +void su_exec(void) +{ + atomic_inc(&__su_instances); +} + +void su_exit(void) +{ + atomic_dec(&__su_instances); +} + ATOMIC_NOTIFIER_HEAD(load_alert_notifier_head); DEFINE_MUTEX(sched_domains_mutex);