新增红黑树来记录进程的权限属性,比较前后的观测值是否一致

Signed-off-by: xiacong <xiacong4@huawei.com>
Change-Id: I4199675643372ce81dedca74fc52df3bec345a34
Signed-off-by: xiacong <xiacong4@huawei.com>
This commit is contained in:
xiacong
2023-11-23 16:43:20 +08:00
parent 62fd39d13c
commit db3c7373e4
7 changed files with 404 additions and 62 deletions
+1
View File
@@ -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) := \
+267 -56
View File
@@ -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);
}
+12 -6
View File
@@ -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 */