exynos-linux-stable/drivers/base/superuser.c
Denis Efremov 8494bca233
android: Add superuser driver
Run "su" to get root.

HELLO NAÏVE TECH JOURNALISTS: This commit here introduces a driver used
during development, because having access to scary debugging facilities
is useful during kernel development. This driver is disabled by default,
and when it is enabled, it spews warnings all over the place to
encourage people who turn it on by accident to turn it off. It's
exceedingly unlikely that somebody turns this on without intending to
do so. You really have to fish around and make a concerted effort to get
it enabled and working, and the warning messages it gives during the build
and boot processes are really an eyesore. So, if you've found this
commit because some idiot shipped a kernel to their users, you have
every reason to blame that idiot, and not this commit. And if the fool
insists it was an 'accident', he's not telling the truth.

This is from:
  https://git.zx2c4.com/kernel-assisted-superuser/about

Signed-off-by: Denis Efremov <efremov@linux.com>
2023-02-21 00:24:42 +03:00

143 lines
No EOL
4.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
/* Hello. If this is enabled in your kernel for some reason, whoever is
* distributing your kernel to you is a complete moron, and you shouldn't
* use their kernel anymore. But it's not my fault! People: don't enable
* this driver! (Note that the existence of this file does not imply the
* driver is actually in use. Look in your .config to see whether this is
* enabled.) -Jason
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mman.h>
#include <linux/ptrace.h>
#include <linux/syscalls.h>
static bool is_su(const char __user *filename)
{
static const char su_path[] = "/system/bin/su";
char ufn[sizeof(su_path)];
return likely(!copy_from_user(ufn, filename, sizeof(ufn))) &&
unlikely(!memcmp(ufn, su_path, sizeof(ufn)));
}
static void __user *userspace_stack_buffer(const void *d, size_t len)
{
/* To avoid having to mmap a page in userspace, just write below the stack pointer. */
char __user *p = (void __user *)current_user_stack_pointer() - len;
return copy_to_user(p, d, len) ? NULL : p;
}
static char __user *sh_user_path(void)
{
static const char sh_path[] = "/system/bin/sh";
return userspace_stack_buffer(sh_path, sizeof(sh_path));
}
static long(*old_newfstatat)(int dfd, const char __user *filename,
struct stat *statbuf, int flag);
static long new_newfstatat(int dfd, const char __user *filename,
struct stat __user *statbuf, int flag)
{
if (!is_su(filename))
return old_newfstatat(dfd, filename, statbuf, flag);
return old_newfstatat(dfd, sh_user_path(), statbuf, flag);
}
static long(*old_faccessat)(int dfd, const char __user *filename, int mode);
static long new_faccessat(int dfd, const char __user *filename, int mode)
{
if (!is_su(filename))
return old_faccessat(dfd, filename, mode);
return old_faccessat(dfd, sh_user_path(), mode);
}
extern int selinux_enforcing;
static long (*old_execve)(const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp);
static long new_execve(const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp)
{
static const char now_root[] = "You are now root.\n";
struct cred *cred;
if (!is_su(filename))
return old_execve(filename, argv, envp);
if (!old_execve(filename, argv, envp))
return 0;
/* It might be enough to just change the security ctx of the
* current task, but that requires slightly more thought than
* just axing the whole thing here.
*/
selinux_enforcing = 0;
/* Rather than the usual commit_creds(prepare_kernel_cred(NULL)) idiom,
* we manually zero out the fields in our existing one, so that we
* don't have to futz with the task's key ring for disk access.
*/
cred = (struct cred *)__task_cred(current);
memset(&cred->uid, 0, sizeof(cred->uid));
memset(&cred->gid, 0, sizeof(cred->gid));
memset(&cred->suid, 0, sizeof(cred->suid));
memset(&cred->euid, 0, sizeof(cred->euid));
memset(&cred->egid, 0, sizeof(cred->egid));
memset(&cred->fsuid, 0, sizeof(cred->fsuid));
memset(&cred->fsgid, 0, sizeof(cred->fsgid));
memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable));
memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted));
memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective));
memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset));
memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient));
sys_write(2, userspace_stack_buffer(now_root, sizeof(now_root)),
sizeof(now_root) - 1);
return old_execve(sh_user_path(), argv, envp);
}
extern const unsigned long sys_call_table[];
static void read_syscall(void **ptr, unsigned int syscall)
{
*ptr = READ_ONCE(*((void **)sys_call_table + syscall));
}
static void replace_syscall(unsigned int syscall, void *ptr)
{
WRITE_ONCE(*((void **)sys_call_table + syscall), ptr);
}
#define read_and_replace_syscall(name) do { \
read_syscall((void **)&old_ ## name, __NR_ ## name); \
replace_syscall(__NR_ ## name, &new_ ## name); \
} while (0)
static int superuser_init(void)
{
pr_err("WARNING WARNING WARNING WARNING WARNING\n");
pr_err("This kernel has kernel-assisted superuser and contains a\n");
pr_err("trivial way to get root. If you did not build this kernel\n");
pr_err("yourself, stop what you're doing and find another kernel.\n");
pr_err("This one is not safe to use.\n");
pr_err("WARNING WARNING WARNING WARNING WARNING\n");
read_and_replace_syscall(newfstatat);
read_and_replace_syscall(faccessat);
read_and_replace_syscall(execve);
return 0;
}
module_init(superuser_init);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Kernel-assisted superuser for Android");
MODULE_AUTHOR("Jason A. Donenfeld <Jason@zx2c4.com>");