mirror of
https://github.com/openharmony/kernel_linux_common_modules.git
synced 2026-08-26 17:16:37 -04:00
新增红黑树来记录进程的权限属性,比较前后的观测值是否一致
Signed-off-by: xiacong <xiacong4@huawei.com> Change-Id: I4199675643372ce81dedca74fc52df3bec345a34 Signed-off-by: xiacong <xiacong4@huawei.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
# Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
#
|
||||
obj-$(CONFIG_SECURITY_CONTAINER_ESCAPE_DETECTION) += core/ced_detection.o
|
||||
obj-$(CONFIG_SECURITY_CONTAINER_ESCAPE_DETECTION) += core/ced_permission.o
|
||||
obj-$(CONFIG_SECURITY_CONTAINER_ESCAPE_DETECTION) += core/ced_module.o
|
||||
|
||||
ccflags-$(CONFIG_SECURITY_CONTAINER_ESCAPE_DETECTION) := \
|
||||
|
||||
@@ -8,121 +8,332 @@
|
||||
#include "avc.h"
|
||||
#include "objsec.h"
|
||||
#include "ced_detection.h"
|
||||
#include "ced_detection_points.h"
|
||||
|
||||
enum ced_event_type {
|
||||
EVENT_CRED_ROOT,
|
||||
EVENT_NSPROXY_ROOT,
|
||||
EVENT_HAS_ROOT,
|
||||
EVENT_OK,
|
||||
EVENT_CRED_CHANGED,
|
||||
EVENT_NSPROXY_CHANGED,
|
||||
EVENT_ATTRIBUTE_CHANGED,
|
||||
EVENT_TREE_CHANGED,
|
||||
EVENT_NUM
|
||||
};
|
||||
|
||||
extern struct task_struct init_task;
|
||||
static struct rb_root root_tree = RB_ROOT;
|
||||
static struct rw_semaphore point_lock;
|
||||
|
||||
static const char *gEventContent[EVENT_NUM] = {
|
||||
"cred has been changed to root.",
|
||||
"nsproxy has been changed to init.",
|
||||
"process has been rooted."
|
||||
static const char *gEventContent[EVENT_NUM - 1] = {
|
||||
"cred has been changed illegally.",
|
||||
"nsproxy has been changed illegally.",
|
||||
"attribute has been changed illegally.",
|
||||
"tree has been changed illegally",
|
||||
};
|
||||
|
||||
static inline void print_container_escape_detection(enum ced_event_type type)
|
||||
{
|
||||
if (type < EVENT_NUM) {
|
||||
ced_log_error("tgid is %d, %s container escape is detected!!!!", current->tgid, gEventContent[type]);
|
||||
}
|
||||
if (type < EVENT_NUM)
|
||||
ced_log_error("tgid is %d, %s container escape is detected!!!!", current->tgid, gEventContent[type - 1]);
|
||||
}
|
||||
|
||||
static int ced_avc_has_perm(u16 tclass, u32 requested)
|
||||
{
|
||||
struct av_decision avd;
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
if (!selinux_initialized(&selinux_state))
|
||||
return 1;
|
||||
|
||||
u32 sid = current_sid();
|
||||
rc = avc_has_perm_noaudit(&selinux_state, sid, sid, tclass, requested,
|
||||
AVC_STRICT, &avd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool ced_has_check_perm(void)
|
||||
bool ced_has_check_perm(void)
|
||||
{
|
||||
// use selinux label to tell the process is hap process
|
||||
int rc = ced_avc_has_perm(SECCLASS_CED, CED__CONTAINER_ESCAPE_CHECK);
|
||||
if (rc) {
|
||||
if (rc)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t process_ns_pac_hash(const struct nsproxy *nsproxy)
|
||||
static struct point_info *point_search(pid_t tgid)
|
||||
{
|
||||
uint64_t pac_hash = 0;
|
||||
uintptr_t ns_ptr = (uintptr_t)nsproxy->mnt_ns;
|
||||
pac_hash ^= ns_ptr;
|
||||
ns_ptr = (uintptr_t)nsproxy->pid_ns_for_children;
|
||||
pac_hash ^= ns_ptr;
|
||||
ns_ptr = (uintptr_t)nsproxy->net_ns;
|
||||
pac_hash ^= ns_ptr;
|
||||
return pac_hash;
|
||||
struct rb_node *node = root_tree.rb_node;
|
||||
while (node != NULL) {
|
||||
struct point_info *point = container_of(node, struct point_info, node);
|
||||
pid_t result = point->tgid;
|
||||
if (result > tgid)
|
||||
node = node->rb_left;
|
||||
else if (result < tgid)
|
||||
node = node->rb_right;
|
||||
else
|
||||
return point;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool is_container_process(const struct nsproxy *new)
|
||||
static bool point_insert(pid_t tgid, struct process_info *info)
|
||||
{
|
||||
uint64_t current_pac_hash = process_ns_pac_hash(new);
|
||||
uint64_t init_task_ns_pac = process_ns_pac_hash(init_task.nsproxy);
|
||||
if (current_pac_hash == init_task_ns_pac) {
|
||||
struct rb_node **new = &root_tree.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct point_info *point = NULL;
|
||||
pid_t result;
|
||||
/* Figure out where to put new node */
|
||||
while (*new != NULL) {
|
||||
point = container_of((*new), struct point_info, node);
|
||||
result = point->tgid;
|
||||
parent = *new;
|
||||
if (result > tgid)
|
||||
new = &(*new)->rb_left;
|
||||
else if (result < tgid)
|
||||
new = &(*new)->rb_right;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
point = kmalloc(sizeof(struct point_info), GFP_KERNEL);
|
||||
if (point == NULL)
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
point->tgid = tgid;
|
||||
point->count = 1;
|
||||
point->info = info;
|
||||
|
||||
/* Add new node and rebalance tree. */
|
||||
rb_link_node(&point->node, parent, new);
|
||||
rb_insert_color(&point->node, &root_tree);
|
||||
return true;
|
||||
}
|
||||
|
||||
void point_erase(pid_t pid)
|
||||
{
|
||||
struct point_info *point = point_search(pid);
|
||||
if (point != NULL) {
|
||||
rb_erase(&point->node, &root_tree);
|
||||
kfree(point->info);
|
||||
point->info = NULL;
|
||||
kfree(point);
|
||||
point=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool detection_promotion_privilege(const struct cred *new)
|
||||
static bool has_same_attributes(struct process_info *a, struct process_info *b)
|
||||
{
|
||||
const struct cred *init_cred = get_task_cred(&init_task);
|
||||
bool flag = false;
|
||||
if (new->euid.val == 0 || new->egid.val == 0 || new->fsuid.val == 0
|
||||
|| !memcmp(&new->cap_effective, &init_cred->cap_effective, sizeof(kernel_cap_t))) {
|
||||
flag = true;
|
||||
if (memcmp(a, b, sizeof(struct process_info)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool has_same_cred(const struct cred *a, struct process_info *b)
|
||||
{
|
||||
if (a->euid.val == b->cred.euid && a->egid.val == b->cred.egid
|
||||
&& a->fsuid.val == b->cred.fsuid
|
||||
&& memcmp(&a->cap_effective, &b->cred.cap_effective, sizeof(kernel_cap_t)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool has_same_nsproxy(const struct nsproxy *a, struct process_info *b)
|
||||
{
|
||||
if (a->mnt_ns == b->ns.mnt_ns && a->pid_ns_for_children == b->ns.pid_ns
|
||||
&& a->net_ns == b->ns.net_ns)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ced_initialize(void)
|
||||
{
|
||||
init_rwsem(&point_lock);
|
||||
}
|
||||
|
||||
void setattr_insert_hook(struct task_struct *task)
|
||||
{
|
||||
if (!ced_has_check_perm())
|
||||
return;
|
||||
|
||||
pid_t tgid = task->tgid;
|
||||
struct process_info *info = process_info_record(task);
|
||||
if (info == NULL)
|
||||
return;
|
||||
|
||||
down_read(&point_lock);
|
||||
struct point_info *result = point_search(task->tgid);
|
||||
if (result != NULL) {
|
||||
up_read(&point_lock);
|
||||
kfree(info);
|
||||
print_container_escape_detection(EVENT_TREE_CHANGED);
|
||||
return;
|
||||
}
|
||||
return flag;
|
||||
up_read(&point_lock);
|
||||
|
||||
down_write(&point_lock);
|
||||
bool ret = point_insert(tgid, info);
|
||||
if (!ret) {
|
||||
up_write(&point_lock);
|
||||
kfree(info);
|
||||
ced_log_error("insert point into tree failed");
|
||||
return;
|
||||
}
|
||||
up_write(&point_lock);
|
||||
}
|
||||
|
||||
static int check_tree_and_attribute(pid_t tgid, struct process_info *current_info, struct point_info **point)
|
||||
{
|
||||
struct point_info *result = point_search(tgid);
|
||||
if (result == NULL)
|
||||
return EVENT_TREE_CHANGED;
|
||||
|
||||
if (!has_same_attributes(result->info, current_info)) {
|
||||
return EVENT_ATTRIBUTE_CHANGED;
|
||||
}
|
||||
*point = result;
|
||||
return EVENT_OK;
|
||||
}
|
||||
|
||||
static int check_cred_atrribute(pid_t tgid, const struct cred *new)
|
||||
{
|
||||
struct point_info *result = point_search(tgid);
|
||||
if (result == NULL)
|
||||
return EVENT_TREE_CHANGED;
|
||||
|
||||
if (!has_same_cred(new, result->info))
|
||||
return EVENT_CRED_CHANGED;
|
||||
|
||||
return EVENT_OK;
|
||||
}
|
||||
|
||||
static int check_nsproxy_atrribute(pid_t tgid, const struct nsproxy *new)
|
||||
{
|
||||
struct point_info *result = point_search(tgid);
|
||||
if (result == NULL)
|
||||
return EVENT_TREE_CHANGED;
|
||||
|
||||
if (!has_same_nsproxy(new, result->info))
|
||||
return EVENT_NSPROXY_CHANGED;
|
||||
|
||||
return EVENT_OK;
|
||||
}
|
||||
|
||||
void kernel_clone_hook(struct task_struct *task)
|
||||
{
|
||||
if (!ced_has_check_perm())
|
||||
return;
|
||||
|
||||
struct process_info *info = process_info_record(task);
|
||||
if (info == NULL)
|
||||
return;
|
||||
|
||||
struct point_info *parent = NULL;
|
||||
// if clone_flags & (CLONE_PARENT|CLONE_THREAD)
|
||||
// p->real_parent = current->real_parent else task->real_parent = current
|
||||
pid_t parent_tgid = task->real_parent->tgid;
|
||||
if (task->real_parent == current->real_parent) {
|
||||
parent_tgid = task->tgid;
|
||||
}
|
||||
// check firstly, judge child task's attributes are different from parent task
|
||||
down_read(&point_lock);
|
||||
int ret = check_tree_and_attribute(parent_tgid, info, &parent);
|
||||
up_read(&point_lock);
|
||||
if (ret) {
|
||||
print_container_escape_detection(ret);
|
||||
kfree(info);
|
||||
info = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// if the tgid of thread exist in the tree, it doesn't have to insert
|
||||
// the node into the tree
|
||||
down_write(&point_lock);
|
||||
if (task->tgid == parent->tgid) {
|
||||
parent->count++;
|
||||
up_write(&point_lock);
|
||||
kfree(info);
|
||||
info = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!point_insert(task->tgid, info)) {
|
||||
up_write(&point_lock);
|
||||
kfree(info);
|
||||
ced_log_error("insert point into tree failed");
|
||||
return;
|
||||
}
|
||||
up_write(&point_lock);
|
||||
}
|
||||
|
||||
void switch_task_namespaces_hook(const struct nsproxy *new)
|
||||
{
|
||||
if (!ced_has_check_perm()) {
|
||||
if (new == NULL || !ced_has_check_perm())
|
||||
return;
|
||||
}
|
||||
|
||||
if (new == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_container_process(new)) {
|
||||
print_container_escape_detection(EVENT_NSPROXY_ROOT);
|
||||
}
|
||||
down_read(&point_lock);
|
||||
int ret = check_nsproxy_atrribute(current->tgid, new);
|
||||
up_read(&point_lock);
|
||||
if (ret)
|
||||
print_container_escape_detection(ret);
|
||||
}
|
||||
|
||||
void commit_creds_hook(const struct cred *new)
|
||||
{
|
||||
if (!ced_has_check_perm()) {
|
||||
if (!ced_has_check_perm())
|
||||
return;
|
||||
}
|
||||
|
||||
if (detection_promotion_privilege(new)) {
|
||||
print_container_escape_detection(EVENT_CRED_ROOT);
|
||||
}
|
||||
down_read(&point_lock);
|
||||
int ret = check_cred_atrribute(current->tgid, new);
|
||||
up_read(&point_lock);
|
||||
if (ret)
|
||||
print_container_escape_detection(ret);
|
||||
}
|
||||
|
||||
void detection_hook(struct task_struct *task)
|
||||
{
|
||||
if (!ced_has_check_perm())
|
||||
return;
|
||||
|
||||
struct process_info *info = process_info_record(task);
|
||||
if (info == NULL)
|
||||
return;
|
||||
|
||||
struct point_info *point = NULL;
|
||||
// check whether the value of node is same as task
|
||||
down_read(&point_lock);
|
||||
int ret = check_tree_and_attribute(task->tgid, info, &point);
|
||||
up_read(&point_lock);
|
||||
if (ret) {
|
||||
print_container_escape_detection(ret);
|
||||
}
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
void exit_hook(struct task_struct *task)
|
||||
{
|
||||
if (!ced_has_check_perm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const struct cred *cred = get_task_cred(task);
|
||||
|
||||
if ((!is_container_process(task->nsproxy) || (detection_promotion_privilege(cred)))) {
|
||||
print_container_escape_detection(EVENT_HAS_ROOT);
|
||||
down_read(&point_lock);
|
||||
struct point_info *result = point_search(task->tgid);
|
||||
if (result == NULL) {
|
||||
up_read(&point_lock);
|
||||
print_container_escape_detection(EVENT_TREE_CHANGED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
up_read(&point_lock);
|
||||
|
||||
down_write(&point_lock);
|
||||
result->count--;
|
||||
|
||||
// when thread number is zero, erase the node of tree
|
||||
if (result->count == 0) {
|
||||
point_erase(task->tgid);
|
||||
}
|
||||
up_write(&point_lock);
|
||||
}
|
||||
|
||||
@@ -7,19 +7,25 @@
|
||||
#include <linux/hck/lite_hck_ced.h>
|
||||
#include "ced_detection.h"
|
||||
#include "ced_log.h"
|
||||
#include "ced_permission.h"
|
||||
|
||||
void ced_register_ced_hooks(void)
|
||||
{
|
||||
REGISTER_HCK_LITE_HOOK(ced_detection_lhck, detection_hook);
|
||||
REGISTER_HCK_LITE_HOOK(ced_switch_task_namespaces_lhck, switch_task_namespaces_hook);
|
||||
REGISTER_HCK_LITE_HOOK(ced_commit_creds_lhck, commit_creds_hook);
|
||||
ced_log_info("ced_register_ced_hooks");
|
||||
REGISTER_HCK_LITE_HOOK(ced_setattr_insert_lhck, setattr_insert_hook);
|
||||
REGISTER_HCK_LITE_HOOK(ced_detection_lhck, detection_hook);
|
||||
REGISTER_HCK_LITE_HOOK(ced_switch_task_namespaces_lhck, switch_task_namespaces_hook);
|
||||
REGISTER_HCK_LITE_HOOK(ced_commit_creds_lhck, commit_creds_hook);
|
||||
REGISTER_HCK_LITE_HOOK(ced_exit_lhck, exit_hook);
|
||||
REGISTER_HCK_LITE_HOOK(ced_kernel_clone_lhck, kernel_clone_hook);
|
||||
REGISTER_HCK_LITE_HOOK(ced_switch_task_namespaces_permission_lhck, switch_task_namespaces_permission_hook);
|
||||
ced_log_info("ced_register_ced_hooks");
|
||||
}
|
||||
|
||||
static int __init ced_module_init(void)
|
||||
{
|
||||
ced_register_ced_hooks();
|
||||
return 0;
|
||||
ced_register_ced_hooks();
|
||||
ced_initialize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(ced_module_init);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include "ced_detection.h"
|
||||
#include "ced_log.h"
|
||||
|
||||
void switch_task_namespaces_permission_hook(const struct nsproxy *new, int *ret)
|
||||
{
|
||||
*ret = 0;
|
||||
if (new == NULL)
|
||||
return;
|
||||
|
||||
if (ced_has_check_perm()) {
|
||||
*ret = -EPERM;
|
||||
ced_log_error("switch task namespace is not permitted in container process");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,13 @@
|
||||
#include <linux/cred.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
void ced_initialize(void);
|
||||
void detection_hook(struct task_struct *task);
|
||||
void setattr_insert_hook(struct task_struct *task);
|
||||
void exit_hook(struct task_struct *task);
|
||||
void switch_task_namespaces_hook(const struct nsproxy *new);
|
||||
void commit_creds_hook(const struct cred *new);
|
||||
void kernel_clone_hook(struct task_struct *task);
|
||||
bool ced_has_check_perm(void);
|
||||
|
||||
#endif /* _CED_DETECTION_H */
|
||||
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_CED_DETECTION_POINTS_H
|
||||
#define _LINUX_CED_DETECTION_POINTS_H
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pid.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uidgid.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/path.h>
|
||||
#include <linux/nsproxy.h>
|
||||
|
||||
struct cred_info {
|
||||
uid_t euid;
|
||||
gid_t egid;
|
||||
uid_t fsuid;
|
||||
kernel_cap_t cap_effective;
|
||||
};
|
||||
|
||||
static inline void cred_info_record(struct cred_info *info, const struct cred *cred)
|
||||
{
|
||||
info->euid = cred->euid.val;
|
||||
info->egid = cred->egid.val;
|
||||
info->fsuid = cred->fsuid.val;
|
||||
|
||||
memcpy(&info->cap_effective.cap[0], &cred->cap_effective.cap[0], sizeof(info->cap_effective.cap));
|
||||
}
|
||||
|
||||
struct ns_info {
|
||||
struct mnt_namespace *mnt_ns;
|
||||
struct pid_namespace *pid_ns;
|
||||
struct net *net_ns;
|
||||
};
|
||||
|
||||
static inline void ns_info_record(struct ns_info *info, const struct nsproxy *nsproxy)
|
||||
{
|
||||
if (nsproxy) {
|
||||
info->mnt_ns = nsproxy->mnt_ns;
|
||||
info->pid_ns = nsproxy->pid_ns_for_children;
|
||||
info->net_ns = nsproxy->net_ns;
|
||||
}
|
||||
}
|
||||
|
||||
struct process_info {
|
||||
struct cred_info cred;
|
||||
struct ns_info ns;
|
||||
};
|
||||
|
||||
struct point_info {
|
||||
struct rb_node node;
|
||||
pid_t tgid;
|
||||
uint32_t count;
|
||||
struct process_info *info;
|
||||
};
|
||||
|
||||
static inline struct process_info *process_info_record(struct task_struct *task)
|
||||
{
|
||||
struct process_info *info = NULL;
|
||||
const struct cred *cred = get_task_cred(task);
|
||||
if (cred == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info = kmalloc(sizeof(struct process_info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memset(info, 0, sizeof(struct process_info));
|
||||
|
||||
cred_info_record(&info->cred, cred);
|
||||
|
||||
if (task->nsproxy != NULL) {
|
||||
ns_info_record(&info->ns, task->nsproxy);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
#endif /* _LINUX_CED_DETECTION_POINTS_H */
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _CED_PERMISSION_H
|
||||
#define _CED_PERMISSION_H
|
||||
|
||||
#include <linux/cred.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
void switch_task_namespaces_permission_hook(const struct nsproxy *new, int *ret);
|
||||
|
||||
#endif /* _CED_PERMISSION_H */
|
||||
Reference in New Issue
Block a user