huawei-mrd-kernel/security/check_root/check_root.c

231 lines
5 KiB
C
Raw Permalink Normal View History

/*
* check_root proc and stp trace log save
*
* Copyright (c) 2001-2017, Huawei Tech. Co., Ltd., Zhebo Zhang <zhangzhebo at huawei.com>
* Copyright (C) 2018 Huawei Tech. Co., Ltd., Ningyu Wang <wangningyu at huawei.com>
*
*/
#include <linux/fs.h>
#include <linux/syscalls.h>
#include <linux/time.h>
#include <linux/uaccess.h>
#include <linux/rtc.h>
#include <linux/statfs.h>
#include <linux/vmalloc.h>
#include <linux/stacktrace.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include<chipset_common/security/check_root.h>
#define ANDROID_THIRD_PART_APK_UID 10000
#define AID_SHELL 2000
#define STP_LOG_DIR "/log/stp"
#define STP_LOG_PATH_LEN 64
#define STP_LOG_DIR_MODE 0775
#define STP_LOG_FILE_MODE 0664
#define STP_LOG_SIZE 1024
#define STP_STACK_DEPTH 10
static struct checkroot_ref_cnt checkroot_ref;
uint get_setids_state(void)
{
unsigned int ids = 0;
if (checkroot_ref.setuid)
ids |= CHECKROOT_SETUID_FLAG;
if (checkroot_ref.setgid)
ids |= CHECKROOT_SETGID_FLAG;
if (checkroot_ref.setresuid)
ids |= CHECKROOT_SETRESUID_FLAG;
if (checkroot_ref.setresgid)
ids |= CHECKROOT_SETRESGID_FLAG;
return ids;
}
/* uncomment this for force stop setXid */
/* #define CONFIG_CHECKROOT_FORCE_STOP */
static int checkroot_risk_id(int curr_id, int flag)
{
const struct cred *now;
now = current_cred();
if (curr_id < ANDROID_THIRD_PART_APK_UID && curr_id != AID_SHELL) {
return 0;
}
pr_emerg("check_root: Uid %d, Gid %d, try to Privilege Escalate\n",
now->uid.val, now->gid.val);
#ifdef CONFIG_CHECKROOT_FORCE_STOP
if (curr_id >= ANDROID_THIRD_PART_APK_UID) {
force_sig(SIGKILL, current);
return -1;
}
#endif
if (flag & CHECKROOT_SETUID_FLAG) {
checkroot_ref.setuid++;
}
if (flag & CHECKROOT_SETGID_FLAG) {
checkroot_ref.setgid++;
}
if (flag & CHECKROOT_SETRESUID_FLAG) {
checkroot_ref.setresuid++;
}
if (flag & CHECKROOT_SETRESGID_FLAG) {
checkroot_ref.setresgid++;
}
return 0;
}
int checkroot_setuid(uid_t uid)
{
return checkroot_risk_id((int)uid, CHECKROOT_SETUID_FLAG);
}
int checkroot_setgid(gid_t gid)
{
return checkroot_risk_id((int)gid, CHECKROOT_SETGID_FLAG);
}
int checkroot_setresuid(uid_t uid)
{
return checkroot_risk_id((int)uid, CHECKROOT_SETRESUID_FLAG);
}
int checkroot_setresgid(gid_t gid)
{
return checkroot_risk_id((int)gid, CHECKROOT_SETRESGID_FLAG);
}
static int __init proc_checkroot_init(void)
{
(void)memset(&checkroot_ref, 0, sizeof(checkroot_ref));
return 0;
}
static void __exit proc_checkroot_exit(void)
{
return;
}
static char* get_current_asctime(void)
{
struct timeval tv = {0};
unsigned long long rtc_time;
struct rtc_time tm;
static char asctime[32];
/*1.get rtc timer*/
do_gettimeofday(&tv);
rtc_time = (unsigned long long)tv.tv_sec;
/*2.convert rtc to asctime*/
memset((void *)asctime, 0, sizeof(asctime));
memset((void *)&tm, 0, sizeof(struct rtc_time));
rtc_time_to_tm(rtc_time, &tm);
snprintf(asctime, sizeof(asctime) - 1, "%04d%02d%02d%02d%02d%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
return asctime;
}
static void stp_print_trace_info(char *buf, size_t len, const char *name)
{
int ret;
unsigned long entries[STP_STACK_DEPTH];
struct stack_trace trace = {
.nr_entries = 0,
.entries = entries,
.max_entries = STP_STACK_DEPTH,
.skip = 0
};
if (!buf || !len) {
return;
}
ret = snprintf(buf, len,
"{\"name\":\"%s\",\"detail\":\"time:%s,pid:%d,pcomm:%.20s,tgid:%d,tgcomm:%.20s,stack:",
name, get_current_asctime(), current->pid, current->comm,
current->tgid, current->group_leader->comm);
if (ret >= len) {
goto out;
}
save_stack_trace(&trace);
ret += snprint_stack_trace(buf + ret, len - ret, &trace, 0);
if (ret >= len) {
goto out;
}
snprintf(buf + ret, len - ret, "\"}\n");
out:
buf[len - 1] = '\0';
return;
}
void stp_save_trace_log(const char *name)
{
mm_segment_t old_fs;
int fd = -1;
char *buf = NULL;
char path[STP_LOG_PATH_LEN] = {0};
size_t len, written;
/* judge irq is comment out for all calls of stp_save_trace_log should be
followed by BUG or VENDOR_EXCEPTION */
/* if (in_irq() || in_softirq() || irqs_disabled())
{
return;
} */
old_fs = get_fs();
set_fs(KERNEL_DS);
if (sys_access(STP_LOG_DIR, 0) != 0) {
if (sys_mkdir(STP_LOG_DIR, STP_LOG_DIR_MODE) < 0) {
pr_err("create stp log dir failed!\n");
goto out;
}
}
(void)snprintf(path, sizeof(path) - 1, "%s/%s.log", STP_LOG_DIR, name);
fd = sys_open(path, O_CREAT | O_WRONLY | O_APPEND, STP_LOG_FILE_MODE);
if (fd < 0) {
pr_err("open file %s failed! fd = %d\n", path, fd);
goto out;
}
buf = (char *)vmalloc(STP_LOG_SIZE);
if (!buf) {
pr_err("vmalloc stp log buffer failed\n");
goto nomem;
}
stp_print_trace_info(buf, STP_LOG_SIZE, name);
pr_err("stp trace log: %s", buf);
len = strlen((char *)buf);
written = sys_write(fd, buf, len);
if (written != len) {
pr_err("write %s log failed! %ld/%ld\n", name, written, len);
}
vfree(buf);
sys_sync();
nomem:
sys_close(fd);
out:
set_fs(old_fs);
}
module_init(proc_checkroot_init);
module_exit(proc_checkroot_exit);