1149 lines
29 KiB
C
Executable file
1149 lines
29 KiB
C
Executable file
/*
|
|
* Copyright (c) Huawei Technologies Co., Ltd. 2016-2018. All rights reserved.
|
|
* Description: the hw_rscan_scanner.c for kernel space root scan
|
|
* Author: likun <quentin.lee@huawei.com>
|
|
* likan <likan82@huawei.com>
|
|
* Create: 2016-06-18
|
|
*/
|
|
|
|
#include <chipset_common/security/hw_kernel_stp_interface.h>
|
|
#include "./include/hw_rscan_scanner.h"
|
|
#include "./include/hw_rscan_utils.h"
|
|
#include "./include/hw_rscan_whitelist.h"
|
|
|
|
#define VAR_NOT_USED(variable) do { (void)(variable); } while (0)
|
|
#define KCODE_OFFSET 0
|
|
#define SYSCALL_OFFSET 1
|
|
#define SEHOOKS_OFFSET 2
|
|
#define SESTATUS_OFFSET 3
|
|
|
|
static DEFINE_MUTEX(scanner_lock); /* lint -save -e64 -e785 -e708 -e570 */
|
|
static DEFINE_MUTEX(whitelist_lock); /* lint -save -e64 -e785 -e708 -e570 */
|
|
static const char *TAG = "hw_rscan_scanner";
|
|
static const char *DEFAULT_PROC = "/init";
|
|
static char *g_whitelist_proc = RPROC_WHITE_LIST_STR;
|
|
static int g_rs_data_init = RSCAN_UNINIT;
|
|
static int g_root_scan_hot_fix;
|
|
|
|
static struct item_bits itembits[MAX_NUM_OF_ITEM] = {
|
|
// kcode
|
|
{
|
|
RS_KCODE,
|
|
KERNELCODEBIT,
|
|
D_RSOPID_KCODE,
|
|
},
|
|
// syscall
|
|
{
|
|
RS_SYS_CALL,
|
|
SYSTEMCALLBIT,
|
|
D_RSOPID_SYS_CALL,
|
|
},
|
|
// selinux
|
|
{
|
|
RS_SE_STATUS,
|
|
SESTATUSBIT,
|
|
D_RSOPID_SE_STATUS,
|
|
},
|
|
// se_hook
|
|
{
|
|
RS_SE_HOOKS,
|
|
SEHOOKBIT,
|
|
D_RSOPID_SE_HOOKS,
|
|
},
|
|
// root_proc
|
|
{
|
|
RS_RRPOCS,
|
|
ROOTPROCBIT,
|
|
D_RSOPID_RRPOCS,
|
|
},
|
|
// set_id
|
|
{
|
|
RS_SETID,
|
|
SETIDBIT,
|
|
D_RSOPID_SETID,
|
|
},
|
|
};
|
|
|
|
struct rscan_skip_flags g_rscan_skip_flag = {
|
|
.skip_kcode = NOT_SKIP,
|
|
.skip_syscall = NOT_SKIP,
|
|
.skip_se_hooks = NOT_SKIP,
|
|
.skip_se_status = NOT_SKIP,
|
|
.skip_rprocs = NOT_SKIP,
|
|
.skip_setid = NOT_SKIP,
|
|
};
|
|
|
|
static struct rscan_result_dynamic g_rscan_clean_scan_result;
|
|
#ifdef CONFIG_HW_ROOT_SCAN_ENG_DEBUG
|
|
static int g_r_p_flag;
|
|
static int g_test_count;
|
|
static struct rscan_result_dynamic g_rscan_orig_result;
|
|
#define FAULT_PRIVATE_LENTH 500
|
|
struct fault_private {
|
|
size_t len;
|
|
char buf[FAULT_PRIVATE_LENTH];
|
|
};
|
|
#endif
|
|
|
|
static int rscan_trigger_by_stp(char *upload_rootproc)
|
|
{
|
|
int scan_err_code = 0;
|
|
int root_masks;
|
|
int dynamic_ops;
|
|
int root_proc_length;
|
|
struct rscan_result_dynamic *scan_result_buf;
|
|
|
|
scan_result_buf = vmalloc(sizeof(struct rscan_result_dynamic));
|
|
if (scan_result_buf == NULL) {
|
|
RSLogError(TAG, "no enough space for scan_result_buf");
|
|
return -ENOSPC;
|
|
}
|
|
memset(scan_result_buf, 0, sizeof(struct rscan_result_dynamic));
|
|
|
|
dynamic_ops = RSOPID_ALL;
|
|
mutex_lock(&scanner_lock);
|
|
root_masks = rscan_dynamic(dynamic_ops, scan_result_buf, &scan_err_code);
|
|
mutex_unlock(&scanner_lock);
|
|
if (root_masks != 0) {
|
|
RSLogDebug(TAG, "root status trigger by stp is %d.",
|
|
root_masks);
|
|
}
|
|
|
|
if ((upload_rootproc != NULL) &&
|
|
(strlen(scan_result_buf->rprocs) > 0)) {
|
|
root_proc_length = strnlen(scan_result_buf->rprocs,
|
|
sizeof(scan_result_buf->rprocs));
|
|
if (root_proc_length >= RPROC_VALUE_LEN_MAX) {
|
|
root_proc_length = RPROC_VALUE_LEN_MAX - 1;
|
|
scan_result_buf->rprocs[root_proc_length] = '\0';
|
|
}
|
|
strncpy(upload_rootproc, scan_result_buf->rprocs,
|
|
RPROC_VALUE_LEN_MAX);
|
|
}
|
|
|
|
vfree(scan_result_buf);
|
|
|
|
return root_masks;
|
|
}
|
|
|
|
static int get_credible_of_item(int item_ree_status, int item_tee_status)
|
|
{
|
|
if ((item_ree_status == 0) && (item_tee_status == 1)) {
|
|
return STP_REFERENCE;
|
|
} else {
|
|
return STP_CREDIBLE;
|
|
}
|
|
}
|
|
|
|
static int need_to_upload(unsigned int masks, unsigned int mask,
|
|
int ree_status, int tee_status, int flag)
|
|
{
|
|
if (flag == 1) {
|
|
return 1;
|
|
}
|
|
|
|
if ((masks & mask) && ((ree_status != 0) || (tee_status != 0))) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* flag = 0, just upload the abnormal items; flag = 1, upload all items */
|
|
static void upload_to_stp(int ree_status, int tee_status,
|
|
char *rootproc, unsigned int mask, int flag)
|
|
{
|
|
int item_status;
|
|
int item_version = 0;
|
|
int item_credible;
|
|
int item_tee_status;
|
|
int need_upload;
|
|
int i;
|
|
|
|
struct stp_item_info *stp_item_info = NULL;
|
|
struct stp_item item = { 0 };
|
|
|
|
for (i = 0; i < MAX_NUM_OF_ITEM; i++) {
|
|
item_status = check_status(ree_status,
|
|
itembits[i].item_ree_bit);
|
|
item_tee_status = check_status(tee_status,
|
|
itembits[i].item_tee_bit);
|
|
need_upload = need_to_upload(mask, itembits[i].item_ree_mask,
|
|
item_status, item_tee_status, flag);
|
|
if (need_upload == 0) {
|
|
continue;
|
|
}
|
|
stp_item_info = get_item_info_by_idx(i);
|
|
if (stp_item_info == NULL) {
|
|
RSLogError(TAG, "idx is %d, get item info by index failed", i);
|
|
return;
|
|
}
|
|
|
|
item_credible = get_credible_of_item(item_status, item_tee_status);
|
|
if ((i == ROOT_PROCS) || (i == SE_HOOK)) {
|
|
item_credible = STP_REFERENCE;
|
|
}
|
|
|
|
if (i == KCODE) {
|
|
if ((item_credible == STP_REFERENCE) &&
|
|
(g_root_scan_hot_fix != 0)) {
|
|
item_credible = STP_CREDIBLE;
|
|
}
|
|
}
|
|
|
|
item.id = stp_item_info->id;
|
|
item.status = item_status;
|
|
item.credible = item_credible;
|
|
item.version = item_version;
|
|
|
|
if (strlen(stp_item_info->name) >= STP_ITEM_NAME_LEN) {
|
|
RSLogError(TAG, "the length of the item name [%s] has exceeded the max allowed value",
|
|
stp_item_info->name);
|
|
return;
|
|
}
|
|
strncpy(item.name, stp_item_info->name, STP_ITEM_NAME_LEN - 1);
|
|
item.name[STP_ITEM_NAME_LEN - 1] = '\0';
|
|
|
|
if (i == ROOT_PROCS) {
|
|
(void)kernel_stp_upload(item, rootproc);
|
|
} else {
|
|
(void)kernel_stp_upload(item, NULL);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int stp_rscan_trigger(void)
|
|
{
|
|
int ree_status;
|
|
int tee_status;
|
|
char *upload_rootproc;
|
|
|
|
upload_rootproc = kzalloc(RPROC_VALUE_LEN_MAX, GFP_KERNEL);
|
|
if (upload_rootproc == NULL) {
|
|
RSLogError(TAG, "failed to alloc upload_rootproc");
|
|
return -ENOSPC;
|
|
}
|
|
|
|
ree_status = rscan_trigger_by_stp(upload_rootproc);
|
|
tee_status = get_tee_status();
|
|
|
|
/* 1 is flag, 1 mean upload all items */
|
|
upload_to_stp(ree_status, tee_status, upload_rootproc, RSOPID_ALL, 1);
|
|
|
|
kfree(upload_rootproc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rscan_dynamic_raw_unlock(uint op_mask,
|
|
struct rscan_result_dynamic *result)
|
|
{
|
|
int ret = 0;
|
|
int error_code = 0;
|
|
|
|
#ifdef CONFIG_HW_ROOT_SCAN_ENG_DEBUG
|
|
if (g_r_p_flag == 1) {
|
|
if (g_rscan_skip_flag.skip_kcode == SKIP) {
|
|
RSLogDebug(TAG, "skip kcode scan.");
|
|
}
|
|
if (g_rscan_skip_flag.skip_syscall == SKIP) {
|
|
RSLogDebug(TAG, "skip syscall scan.");
|
|
}
|
|
if (g_rscan_skip_flag.skip_se_hooks == SKIP) {
|
|
RSLogDebug(TAG, "skip se hooks scan.");
|
|
}
|
|
if (g_rscan_skip_flag.skip_se_status == SKIP) {
|
|
RSLogDebug(TAG, "skip se status scan.");
|
|
}
|
|
if (g_rscan_skip_flag.skip_setid == SKIP) {
|
|
RSLogDebug(TAG, "skip setid scan.");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (op_mask & D_RSOPID_KCODE) {
|
|
ret = kcode_scan(result->kcode);
|
|
if (ret != 0) {
|
|
error_code |= D_RSOPID_KCODE;
|
|
RSLogError(TAG, "kcode_scan failed");
|
|
}
|
|
}
|
|
|
|
if (op_mask & D_RSOPID_SYS_CALL) {
|
|
ret = kcode_syscall_scan(result->syscalls);
|
|
if (ret != 0) {
|
|
error_code |= D_RSOPID_SYS_CALL;
|
|
RSLogError(TAG, "kcode system call scan failed");
|
|
}
|
|
}
|
|
|
|
if (op_mask & D_RSOPID_SE_HOOKS) {
|
|
ret = sescan_hookhash(result->sehooks);
|
|
if (ret != 0) {
|
|
error_code |= D_RSOPID_SE_HOOKS;
|
|
RSLogError(TAG, "sescan_hookhash scan failed");
|
|
}
|
|
}
|
|
|
|
if (op_mask & D_RSOPID_SE_STATUS) {
|
|
result->seenforcing = get_selinux_enforcing();
|
|
}
|
|
|
|
if (op_mask & D_RSOPID_RRPOCS) {
|
|
#ifdef CONFIG_RSCAN_SKIP_RPROCS
|
|
strncpy(result->rprocs, "/init", sizeof(result->rprocs));
|
|
#else
|
|
ret = get_root_procs(result->rprocs, sizeof(result->rprocs));
|
|
if (ret == 0) {
|
|
error_code |= D_RSOPID_RRPOCS;
|
|
RSLogError(TAG, "root processes scan failed!");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (op_mask & D_RSOPID_SETID) {
|
|
result->setid = get_setids();
|
|
}
|
|
|
|
return error_code;
|
|
}
|
|
|
|
/* return: mask of abnormal scans items result */
|
|
int rscan_dynamic(uint op_mask, struct rscan_result_dynamic *result,
|
|
int *error_code)
|
|
{
|
|
int bad_mask = 0;
|
|
|
|
if ((result == NULL) || (error_code == NULL)) {
|
|
RSLogError(TAG, "input parameters error!");
|
|
return -EINVAL;
|
|
}
|
|
|
|
*error_code = rscan_dynamic_raw_unlock(op_mask, result);
|
|
if (*error_code != 0) {
|
|
RSLogWarning(TAG, "some item of root scan failed");
|
|
}
|
|
|
|
if ((op_mask & D_RSOPID_KCODE) &&
|
|
(g_rscan_skip_flag.skip_kcode == NOT_SKIP) &&
|
|
(memcmp(result->kcode, g_rscan_clean_scan_result.kcode,
|
|
sizeof(result->kcode)) != 0)) {
|
|
bad_mask |= D_RSOPID_KCODE;
|
|
RSLogDebug(TAG, "kernel code is abnormal");
|
|
}
|
|
|
|
if ((op_mask & D_RSOPID_SYS_CALL) &&
|
|
(g_rscan_skip_flag.skip_syscall == NOT_SKIP) &&
|
|
(memcmp(result->syscalls, g_rscan_clean_scan_result.syscalls,
|
|
sizeof(result->syscalls)) != 0)) {
|
|
bad_mask |= D_RSOPID_SYS_CALL;
|
|
RSLogDebug(TAG, "kernel system call is abnormal");
|
|
}
|
|
|
|
if ((op_mask & D_RSOPID_SE_HOOKS) &&
|
|
(g_rscan_skip_flag.skip_se_hooks == NOT_SKIP) &&
|
|
(memcmp(result->sehooks, g_rscan_clean_scan_result.sehooks,
|
|
sizeof(result->sehooks)) != 0)) {
|
|
bad_mask |= D_RSOPID_SE_HOOKS;
|
|
RSLogDebug(TAG, "SeLinux hooks is abnormal");
|
|
}
|
|
|
|
if ((op_mask & D_RSOPID_SE_STATUS) &&
|
|
(g_rscan_skip_flag.skip_se_status == NOT_SKIP) &&
|
|
(result->seenforcing !=
|
|
g_rscan_clean_scan_result.seenforcing)) {
|
|
bad_mask |= D_RSOPID_SE_STATUS;
|
|
RSLogDebug(TAG, "SeLinux enforcing status is abnormal");
|
|
}
|
|
|
|
if ((op_mask & D_RSOPID_RRPOCS) &&
|
|
(g_rscan_skip_flag.skip_rprocs == NOT_SKIP)) {
|
|
rprocs_strip_whitelist(result->rprocs,
|
|
(ssize_t)sizeof(result->rprocs));
|
|
if (result->rprocs[0]) {
|
|
bad_mask |= D_RSOPID_RRPOCS;
|
|
RSLogDebug(TAG, "root processes are abnormal");
|
|
}
|
|
}
|
|
|
|
if ((op_mask & D_RSOPID_SETID) &&
|
|
(g_rscan_skip_flag.skip_se_status == NOT_SKIP) &&
|
|
(result->setid != g_rscan_clean_scan_result.setid)) {
|
|
bad_mask |= D_RSOPID_SETID;
|
|
RSLogDebug(TAG, "SeLinux enforcing status is abnormal");
|
|
}
|
|
|
|
RSLogTrace(TAG, "root scan finished.");
|
|
return bad_mask;
|
|
}
|
|
|
|
/* just get the measurement, return the error mask */
|
|
int rscan_dynamic_raw(uint op_mask, struct rscan_result_dynamic *result)
|
|
{
|
|
int error_code;
|
|
|
|
if (result == NULL) {
|
|
RSLogError(TAG, "input parameter is invalid");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&scanner_lock);
|
|
error_code = rscan_dynamic_raw_unlock(op_mask, result);
|
|
mutex_unlock(&scanner_lock);
|
|
|
|
return error_code;
|
|
}
|
|
|
|
/* call by CA to send dynamic measurement and upload abnormal item */
|
|
int rscan_dynamic_raw_and_upload(uint op_mask,
|
|
struct rscan_result_dynamic *result)
|
|
{
|
|
int ree_status;
|
|
int tee_status;
|
|
int error_code = 0;
|
|
|
|
if (result == NULL) {
|
|
RSLogError(TAG, "input parameter is invalid");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&scanner_lock);
|
|
ree_status = rscan_dynamic(op_mask, result, &error_code);
|
|
mutex_unlock(&scanner_lock);
|
|
|
|
tee_status = get_tee_status();
|
|
|
|
/* 0 in upload_to_stp mean just upload abnormal items */
|
|
if ((ree_status != 0) || (tee_status != 0)) {
|
|
upload_to_stp(ree_status, tee_status, NULL, op_mask, 0);
|
|
}
|
|
|
|
return error_code;
|
|
}
|
|
|
|
#ifdef CONFIG_ARCH_MSM
|
|
int get_battery_status(int *is_charging, int *percentage)
|
|
{
|
|
union power_supply_propval status;
|
|
union power_supply_propval capacity;
|
|
struct power_supply *psy = power_supply_get_by_name(BATTERY_NAME);
|
|
|
|
if (psy == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (is_charging &&
|
|
!psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) {
|
|
*is_charging = (status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
|
|
(status.intval == POWER_SUPPLY_STATUS_FULL);
|
|
}
|
|
|
|
if (percentage &&
|
|
!psy->get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &capacity)) {
|
|
*percentage = capacity.intval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
int get_battery_status(int *is_charging, int *percentage)
|
|
{
|
|
union power_supply_propval status;
|
|
union power_supply_propval capacity;
|
|
struct power_supply *psy;
|
|
|
|
if ((is_charging == NULL) || (percentage == NULL)) {
|
|
RSLogError(TAG, "input parameters invalid");
|
|
return -EINVAL;
|
|
}
|
|
|
|
psy = power_supply_get_by_name(BATTERY_NAME);
|
|
if (psy == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
/* is_charging never be NULL because of input parameters check */
|
|
if (!power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) {
|
|
*is_charging = (status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
|
|
(status.intval == POWER_SUPPLY_STATUS_FULL);
|
|
}
|
|
|
|
/* percentage never be NULL because of input parameters check */
|
|
if (!power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &capacity)) {
|
|
*percentage = capacity.intval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int rscan_get_status(struct rscan_status *status)
|
|
{
|
|
int is_charging = 0;
|
|
int percentage = 0;
|
|
int result = 0;
|
|
struct timeval tv;
|
|
|
|
if (status == NULL) {
|
|
RSLogError(TAG, "input parameter is invalid");
|
|
return -1;
|
|
}
|
|
|
|
status->cpuload = 0;
|
|
|
|
if (get_battery_status(&is_charging, &percentage) == 0) {
|
|
status->battery = (uint32_t)percentage;
|
|
status->charging = (uint32_t)is_charging;
|
|
} else {
|
|
RSLogWarning(TAG, "rootscan: get_battery_status failed");
|
|
status->battery = 0;
|
|
status->charging = 0;
|
|
result = 1;
|
|
}
|
|
|
|
do_gettimeofday(&tv);
|
|
status->time = (uint32_t)tv.tv_sec;
|
|
status->timezone = (uint32_t)sys_tz.tz_minuteswest;
|
|
|
|
return result;
|
|
}
|
|
|
|
int load_rproc_whitelist(char *whitelist, size_t len)
|
|
{
|
|
size_t min_len = strlen(g_whitelist_proc);
|
|
|
|
if (whitelist == NULL) {
|
|
RSLogError(TAG, "input parameter is invalid");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (min_len >= len) {
|
|
RSLogWarning(TAG, "The g_whitelist_proc lenth is too long");
|
|
return -1;
|
|
} else if (min_len <= 0) {
|
|
RSLogWarning(TAG, "g_whitelist_proc is null");
|
|
return -1;
|
|
} else {
|
|
strncpy(whitelist, g_whitelist_proc, min_len);
|
|
whitelist[min_len] = '\0';
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rscan_init_data(void)
|
|
{
|
|
int ret;
|
|
|
|
/* initialize g_rscan_clean_scan_result */
|
|
memset(&g_rscan_clean_scan_result, 0, sizeof(struct rscan_result_dynamic));
|
|
|
|
/* 1:selinux closed 0:selinux opened */
|
|
g_rscan_clean_scan_result.seenforcing = 1;
|
|
g_rscan_clean_scan_result.setid = get_setids();
|
|
|
|
ret = load_rproc_whitelist(g_rscan_clean_scan_result.rprocs,
|
|
sizeof(g_rscan_clean_scan_result.rprocs));
|
|
if ((ret != 0) || (!init_rprocs_whitelist(g_rscan_clean_scan_result.rprocs))) {
|
|
RSLogError(TAG, "load root whitelist failed, rproc will skip");
|
|
strncpy(g_rscan_clean_scan_result.rprocs, DEFAULT_PROC,
|
|
strlen(DEFAULT_PROC));
|
|
g_rscan_skip_flag.skip_rprocs = SKIP;
|
|
}
|
|
|
|
ret = rscan_dynamic_raw(D_RSOPID_KCODE |
|
|
D_RSOPID_SYS_CALL |
|
|
D_RSOPID_SE_HOOKS,
|
|
&g_rscan_clean_scan_result);
|
|
if (ret != 0) {
|
|
if (ret & D_RSOPID_KCODE) {
|
|
RSLogError(TAG, "rscan D_RSOPID_KCODE init failed");
|
|
g_rscan_skip_flag.skip_kcode = SKIP;
|
|
}
|
|
|
|
if (ret & D_RSOPID_SYS_CALL) {
|
|
RSLogError(TAG, "rscan D_RSOPID_SYS_CALL init failed");
|
|
g_rscan_skip_flag.skip_syscall = SKIP;
|
|
}
|
|
|
|
if (ret & D_RSOPID_SE_HOOKS) {
|
|
RSLogError(TAG, "rscan D_RSOPID_SE_HOOKS init failed");
|
|
g_rscan_skip_flag.skip_se_hooks = SKIP;
|
|
}
|
|
}
|
|
|
|
g_rs_data_init = RSCAN_INIT;
|
|
return 0;
|
|
}
|
|
|
|
int rscan_trigger(void)
|
|
{
|
|
int result = stp_rscan_trigger();
|
|
|
|
RSLogTrace(TAG, "scan and upload finished. result: %d", result);
|
|
return result;
|
|
}
|
|
|
|
static int dynamic_call(unsigned int mask)
|
|
{
|
|
int root_status = rscan_dynamic_raw_unlock(mask,
|
|
&g_rscan_clean_scan_result);
|
|
|
|
RSLogDebug(TAG, "set %d scan resume", mask);
|
|
|
|
if (root_status != 0) {
|
|
return root_status;
|
|
}
|
|
|
|
if (mask & D_RSOPID_KCODE) {
|
|
g_rscan_skip_flag.skip_kcode = NOT_SKIP;
|
|
}
|
|
|
|
if (mask & D_RSOPID_SYS_CALL) {
|
|
g_rscan_skip_flag.skip_syscall = NOT_SKIP;
|
|
}
|
|
|
|
if (mask & D_RSOPID_SE_HOOKS) {
|
|
g_rscan_skip_flag.skip_se_hooks = NOT_SKIP;
|
|
}
|
|
|
|
if (mask & D_RSOPID_SE_STATUS) {
|
|
g_rscan_skip_flag.skip_se_status = NOT_SKIP;
|
|
}
|
|
|
|
return root_status;
|
|
}
|
|
|
|
/* @reserved is reserved parameters for external module */
|
|
static int __root_scan_pause(unsigned int op_mask, void *reserved)
|
|
{
|
|
VAR_NOT_USED(reserved);
|
|
|
|
g_rscan_skip_flag.skip_kcode = SKIP &
|
|
((op_mask & D_RSOPID_KCODE) >> KCODE_OFFSET);
|
|
g_rscan_skip_flag.skip_syscall = SKIP &
|
|
((op_mask & D_RSOPID_SYS_CALL) >> SYSCALL_OFFSET);
|
|
g_rscan_skip_flag.skip_se_hooks = SKIP & ((op_mask &
|
|
D_RSOPID_SE_HOOKS) >> SEHOOKS_OFFSET);
|
|
g_rscan_skip_flag.skip_se_status = SKIP &
|
|
((op_mask & D_RSOPID_SE_STATUS) >> SESTATUS_OFFSET);
|
|
|
|
RSLogDebug(TAG, "set scan pause, pause mask %d", op_mask);
|
|
|
|
#ifdef CONFIG_HW_ROOT_SCAN_ENG_DEBUG
|
|
g_r_p_flag = 1;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* @reserved is reserved parameters for external module */
|
|
static int __root_scan_resume(unsigned int op_mask, void *reserved)
|
|
{
|
|
unsigned int resume_mask = 0;
|
|
|
|
VAR_NOT_USED(reserved);
|
|
#ifdef CONFIG_HW_ROOT_SCAN_ENG_DEBUG
|
|
g_r_p_flag = 0;
|
|
#endif
|
|
|
|
if ((op_mask & D_RSOPID_KCODE) &&
|
|
(g_rscan_skip_flag.skip_kcode == SKIP)) {
|
|
resume_mask |= D_RSOPID_KCODE;
|
|
}
|
|
|
|
if ((op_mask & D_RSOPID_SYS_CALL) &&
|
|
(g_rscan_skip_flag.skip_syscall == SKIP)) {
|
|
resume_mask |= D_RSOPID_SYS_CALL;
|
|
}
|
|
|
|
if ((op_mask & D_RSOPID_SE_HOOKS) &&
|
|
(g_rscan_skip_flag.skip_se_hooks == SKIP)) {
|
|
resume_mask |= D_RSOPID_SE_HOOKS;
|
|
}
|
|
|
|
if ((op_mask & D_RSOPID_SE_STATUS) &&
|
|
(g_rscan_skip_flag.skip_se_status == SKIP)) {
|
|
resume_mask |= D_RSOPID_SE_STATUS;
|
|
}
|
|
|
|
return dynamic_call(resume_mask);
|
|
}
|
|
|
|
/* @reserved is reserved parameters for external module */
|
|
int root_scan_pause(unsigned int op_mask, void *reserved)
|
|
{
|
|
int scan_err_code = 0;
|
|
int result = 0;
|
|
int root_status;
|
|
int dynamic_ops;
|
|
struct rscan_result_dynamic *scan_result_buf;
|
|
struct timeval tv;
|
|
|
|
VAR_NOT_USED(reserved);
|
|
|
|
do_gettimeofday(&tv);
|
|
RSLogTrace(TAG, "pause item:%d, time:%ld:%ld",
|
|
op_mask, tv.tv_sec, tv.tv_usec);
|
|
|
|
mutex_lock(&scanner_lock);
|
|
scan_result_buf = vmalloc(sizeof(struct rscan_result_dynamic));
|
|
if (scan_result_buf == NULL) {
|
|
RSLogError(TAG, "no enough space for scan_result_buf");
|
|
mutex_unlock(&scanner_lock);
|
|
return -ENOSPC;
|
|
}
|
|
memset(scan_result_buf, 0, sizeof(struct rscan_result_dynamic));
|
|
|
|
/* do scan before pause rootscan */
|
|
dynamic_ops = D_RSOPID_KCODE | D_RSOPID_SYS_CALL |
|
|
D_RSOPID_SE_HOOKS | D_RSOPID_SE_STATUS;
|
|
root_status = rscan_dynamic(dynamic_ops, scan_result_buf, &scan_err_code);
|
|
if (root_status == 0) {
|
|
RSLogTrace(TAG, "environment clean ,pause root scan go");
|
|
result = __root_scan_pause(op_mask, reserved);
|
|
} else {
|
|
RSLogTrace(TAG, "already rooted, skip pause.");
|
|
result = root_status;
|
|
}
|
|
mutex_unlock(&scanner_lock);
|
|
|
|
/* scan_result_buf never evaluates to NULL */
|
|
vfree(scan_result_buf);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* @reserved is reserved parameters for external module */
|
|
int root_scan_resume(unsigned int op_mask, void *reserved)
|
|
{
|
|
struct timeval tv;
|
|
int result;
|
|
|
|
VAR_NOT_USED(reserved);
|
|
|
|
g_root_scan_hot_fix = 1; /* have been done HotFix */
|
|
|
|
do_gettimeofday(&tv);
|
|
RSLogTrace(TAG, "resume item:%d, time:%ld:%ld",
|
|
op_mask, tv.tv_sec, tv.tv_usec);
|
|
|
|
mutex_lock(&scanner_lock);
|
|
result = __root_scan_resume(op_mask, reserved);
|
|
mutex_unlock(&scanner_lock);
|
|
return result;
|
|
}
|
|
|
|
#ifdef CONFIG_HW_ROOT_SCAN_ENG_DEBUG
|
|
static ssize_t copy_private_data_to_user(struct file *file,
|
|
char __user *buf, size_t count, loff_t *offp)
|
|
{
|
|
struct fault_private *priv = file->private_data;
|
|
size_t tocopy = 0;
|
|
|
|
if (priv == NULL) {
|
|
RSLogError(TAG, "priv is NULL");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (*offp < priv->len) {
|
|
tocopy = ((priv->len - *offp) < count) ? (priv->len - *offp) : count;
|
|
|
|
if (copy_to_user(buf, priv->buf + *offp, tocopy)) {
|
|
RSLogError(TAG, "copy_to_user failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
*offp += tocopy;
|
|
}
|
|
return tocopy;
|
|
}
|
|
|
|
static int open_private_file(struct file *file, char *strBuf)
|
|
{
|
|
struct fault_private *priv;
|
|
|
|
WARN_ON(file->private_data);
|
|
priv = vmalloc(sizeof(struct fault_private));
|
|
if (priv == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
file->private_data = priv;
|
|
memset(priv, 0, sizeof(struct fault_private));
|
|
priv->len = strlen(strBuf);
|
|
if (priv->len >= FAULT_PRIVATE_LENTH) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
strncpy(priv->buf, strBuf, priv->len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int kcode_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = open_private_file(file, "kernel code scan testing\n");
|
|
|
|
RSLogDebug(TAG, "root scan kernel code result:%d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t kcode_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
memset(g_rscan_clean_scan_result.kcode, 0,
|
|
sizeof(g_rscan_clean_scan_result.kcode));
|
|
RSLogDebug(TAG, "rootscan: dead_code injected\n");
|
|
return copy_private_data_to_user(file, buf, count, offp);
|
|
}
|
|
|
|
static int kcode_release(struct inode *inode, struct file *file)
|
|
{
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "xxx. kcode_release succ!");
|
|
return 0;
|
|
}
|
|
|
|
static int rs_pause_test_open(struct inode *inode, struct file *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t rs_pause_test_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
int test = 0;
|
|
int pause_result = root_scan_pause(D_RSOPID_KCODE | D_RSOPID_SYS_CALL |
|
|
D_RSOPID_SE_HOOKS | D_RSOPID_SE_STATUS, &test);
|
|
g_test_count++;
|
|
RSLogDebug(TAG, "root scan test, rs_pause_test, count:%d, result:%d",
|
|
g_test_count, pause_result);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rs_pause_test_release(struct inode *inode, struct file *file)
|
|
{
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "xxx. pause release succ!");
|
|
return 0;
|
|
}
|
|
|
|
static int rs_resume_test_open(struct inode *inode, struct file *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t rs_resume_test_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
int test = 0;
|
|
int resume_result = root_scan_resume((D_RSOPID_KCODE |
|
|
D_RSOPID_SYS_CALL |
|
|
D_RSOPID_SE_HOOKS |
|
|
D_RSOPID_SE_STATUS), &test);
|
|
g_test_count++;
|
|
RSLogDebug(TAG, "root scan test, rs_resume_test, count%d, result:%d",
|
|
g_test_count, resume_result);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rs_resume_test_release(struct inode *inode, struct file *file)
|
|
{
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "xxx. resume release succ!");
|
|
return 0;
|
|
}
|
|
|
|
/* revert kcode test items */
|
|
static int rev_kcode_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = open_private_file(file, "revert kernel code scan testing\n");
|
|
|
|
RSLogDebug(TAG, "root scan revert kernel code result:%d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t rev_kcode_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
memcpy(g_rscan_clean_scan_result.kcode,
|
|
g_rscan_orig_result.kcode, sizeof(g_rscan_orig_result.kcode));
|
|
|
|
RSLogDebug(TAG, "revert rootscan: dead_code reverted\n");
|
|
return copy_private_data_to_user(file, buf, count, offp);
|
|
}
|
|
|
|
static int rev_kcode_release(struct inode *inode, struct file *file)
|
|
{
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "3. rev_sehooks_release succ!");
|
|
return 0;
|
|
}
|
|
|
|
static int se_enforcing_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = open_private_file(file, "se_enforcing scan testing\n");
|
|
|
|
RSLogDebug(TAG, "root scan se_enforcing result:%d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t se_enforcing_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
g_rscan_clean_scan_result.seenforcing = 0;
|
|
RSLogDebug(TAG, "root scan test, se_enforcing broken");
|
|
return copy_private_data_to_user(file, buf, count, offp);
|
|
}
|
|
|
|
static int se_enforcing_release(struct inode *inode, struct file *file)
|
|
{
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "8. se enforcing release succ!");
|
|
return 0;
|
|
}
|
|
|
|
static int syscall_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = open_private_file(file, "system call scan testing\n");
|
|
|
|
RSLogDebug(TAG, "root scan system call result:%d", ret);
|
|
return ret;
|
|
}
|
|
static ssize_t syscall_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
memset(g_rscan_clean_scan_result.syscalls, 0,
|
|
sizeof(g_rscan_clean_scan_result.syscalls));
|
|
RSLogDebug(TAG, "rootscan: syscall injected\n");
|
|
return copy_private_data_to_user(file, buf, count, offp);
|
|
}
|
|
|
|
static int syscall_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct fault_private *priv = file->private_data;
|
|
|
|
if (priv == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "7. syscall_release succ!");
|
|
return 0;
|
|
}
|
|
|
|
/* set selinux hooks to all 0, for test */
|
|
static int sehooks_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = open_private_file(file, "se_hooks scan testing\n");
|
|
|
|
RSLogDebug(TAG, "root scan se_hooks result:%d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t sehooks_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
memset(g_rscan_clean_scan_result.sehooks, 0,
|
|
sizeof(g_rscan_clean_scan_result.sehooks));
|
|
RSLogDebug(TAG, "rootscan: sec->sem_semctl injected");
|
|
return copy_private_data_to_user(file, buf, count, offp);
|
|
}
|
|
|
|
static int sehooks_release(struct inode *inode, struct file *file)
|
|
{
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "5. sehooks_release succ!");
|
|
return 0;
|
|
}
|
|
|
|
/* revert syscall test items */
|
|
static int rev_syscall_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = open_private_file(file, "revert system call scan testing\n");
|
|
|
|
RSLogDebug(TAG, "root scan revert system call result:%d", ret);
|
|
return ret;
|
|
}
|
|
static ssize_t rev_syscall_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
memcpy(g_rscan_clean_scan_result.syscalls,
|
|
g_rscan_orig_result.syscalls, sizeof(g_rscan_orig_result.syscalls));
|
|
|
|
RSLogDebug(TAG, "revert rootscan: syscall reverted");
|
|
return copy_private_data_to_user(file, buf, count, offp);
|
|
}
|
|
|
|
static int rev_syscall_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct fault_private *priv = file->private_data;
|
|
|
|
if (priv == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "4. rev_syscall_release succ!");
|
|
return 0;
|
|
}
|
|
|
|
/* revert selinux test items make selinux hooks data original */
|
|
static int rev_sehooks_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = open_private_file(file, "revert se_hooks scan testing\n");
|
|
|
|
RSLogDebug(TAG, "root scan revert se_hooks result:%d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t rev_sehooks_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
memcpy(g_rscan_clean_scan_result.sehooks,
|
|
g_rscan_orig_result.sehooks, sizeof(g_rscan_orig_result.sehooks));
|
|
|
|
RSLogDebug(TAG, "revert root scan test, sehooks reverted");
|
|
return copy_private_data_to_user(file, buf, count, offp);
|
|
}
|
|
|
|
static int rev_sehooks_release(struct inode *inode, struct file *file)
|
|
{
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "2. rev_sehooks_release succ!");
|
|
return 0;
|
|
}
|
|
|
|
/* revert selinux status test items */
|
|
static int rev_se_enforcing_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = open_private_file(file, "revert se_enforcing scan testing\n");
|
|
|
|
RSLogDebug(TAG, "root scan revert se_enforcing result:%d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t rev_se_enforcing_read(struct file *file, char __user *buf,
|
|
size_t count, loff_t *offp)
|
|
{
|
|
g_rscan_clean_scan_result.seenforcing = 1;
|
|
RSLogDebug(TAG, "revert root scan test, se_enforcing reverted");
|
|
return copy_private_data_to_user(file, buf, count, offp);
|
|
}
|
|
|
|
static int rev_se_enforcing_release(struct inode *inode, struct file *file)
|
|
{
|
|
vfree(file->private_data);
|
|
file->private_data = NULL;
|
|
RSLogDebug(TAG, "1. rev_se_enforcing_release succ!");
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations rs_pause_test_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = rs_pause_test_open,
|
|
.read = rs_pause_test_read,
|
|
.release = rs_pause_test_release,
|
|
};
|
|
|
|
static const struct file_operations rs_resume_test_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = rs_resume_test_open,
|
|
.read = rs_resume_test_read,
|
|
.release = rs_resume_test_release,
|
|
};
|
|
|
|
static const struct file_operations kcode_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = kcode_open,
|
|
.read = kcode_read,
|
|
.release = kcode_release,
|
|
};
|
|
|
|
static const struct file_operations rev_kcode_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = rev_kcode_open,
|
|
.read = rev_kcode_read,
|
|
.release = rev_kcode_release,
|
|
};
|
|
|
|
static const struct file_operations se_enforcing_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = se_enforcing_open,
|
|
.read = se_enforcing_read,
|
|
.release = se_enforcing_release,
|
|
};
|
|
|
|
static const struct file_operations syscall_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = syscall_open,
|
|
.read = syscall_read,
|
|
.release = syscall_release,
|
|
};
|
|
|
|
static const struct file_operations sehooks_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = sehooks_open,
|
|
.read = sehooks_read,
|
|
.release = sehooks_release,
|
|
};
|
|
|
|
static const struct file_operations rev_syscall_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = rev_syscall_open,
|
|
.read = rev_syscall_read,
|
|
.release = rev_syscall_release,
|
|
};
|
|
|
|
static const struct file_operations rev_sehooks_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = rev_sehooks_open,
|
|
.read = rev_sehooks_read,
|
|
.release = rev_sehooks_release,
|
|
};
|
|
|
|
static const struct file_operations rev_se_enforcing_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = rev_se_enforcing_open,
|
|
.read = rev_se_enforcing_read,
|
|
.release = rev_se_enforcing_release,
|
|
};
|
|
#endif
|
|
|
|
int rscan_dynamic_init(void)
|
|
{
|
|
if (rscan_init_data() != 0) {
|
|
RSLogError(TAG, "rootscan: rscan init data failed");
|
|
return RSCAN_ERR_SCANNER_INIT;
|
|
}
|
|
#ifdef CONFIG_HW_ROOT_SCAN_ENG_DEBUG
|
|
g_rscan_orig_result = g_rscan_clean_scan_result;
|
|
/* 0777 is the permissions format of file, Read, write and excute */
|
|
proc_create("rs_pause_test", 0777, NULL, &rs_pause_test_fops);
|
|
proc_create("rs_resume_test", 0777, NULL, &rs_resume_test_fops);
|
|
|
|
proc_create("rev_sehooks", 0777, NULL, &rev_sehooks_fops);
|
|
proc_create("rev_kcode", 0777, NULL, &rev_kcode_fops);
|
|
proc_create("rev_syscall", 0777, NULL, &rev_syscall_fops);
|
|
proc_create("rev_se_enforcing", 0777, NULL, &rev_se_enforcing_fops);
|
|
|
|
proc_create("rs_se_enforcing", 0777, NULL, &se_enforcing_fops);
|
|
proc_create("rs_sehooks", 0777, NULL, &sehooks_fops);
|
|
proc_create("rs_kcode", 0777, NULL, &kcode_fops);
|
|
proc_create("rs_syscall", 0777, NULL, &syscall_fops);
|
|
#endif
|
|
return 0;
|
|
}
|