integrity: add dynamic integrity measurement (DIM) support
This commit adds the Dynamic Integrity Measurement (DIM) architecture to security/integrity. DIM is designed to monitor the integrity of executables and running processes by maintaining and verifying hashes. It can also interact with TPM hardware to extend integrity measurements. The DIM architecture consists of two main modules: - dim_core: Provides the core functionality for measuring, monitor and log the integrity of running processes and executables. - dim_monitor: Provides the ability to measure and monitor the integrity of dim_core. Subdirectory overview: - common: Contains shared utilities, hash functions, TPM interaction, and logging for DIM. - core: Implements the "dim_core" module and the main integrity measurement logic, including process measurement, memory management, and policies. - measure: Provides measurement utilities and baseline functions for DIM's integrity checks. - monitor: Implements the "dim_monitor" module. CONFIG options: - CONFIG_DIM: Top-level option to enable the DIM architecture. - CONFIG_DIM_CORE: Enables the "dim_core" module for core functionalities. - CONFIG_DIM_HASH_SUPPORT_SM3: Enables the SM3 hash algorithm for integrity measurement. - CONFIG_DIM_MONITOR: Enables the "dim_monitor" module. The code is from: https://gitee.com/openeuler/dim. Signed-off-by: Sinong Chen <costinchen@tencent.com>
This commit is contained in:
parent
24dfafc175
commit
620860c15a
|
@ -132,5 +132,6 @@ config INTEGRITY_AUDIT
|
|||
|
||||
source "security/integrity/ima/Kconfig"
|
||||
source "security/integrity/evm/Kconfig"
|
||||
source "security/integrity/dim/Kconfig"
|
||||
|
||||
endif # if INTEGRITY
|
||||
|
|
|
@ -20,3 +20,4 @@ integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
|
|||
platform_certs/keyring_handler.o
|
||||
obj-$(CONFIG_IMA) += ima/
|
||||
obj-$(CONFIG_EVM) += evm/
|
||||
obj-$(CONFIG_DIM) += dim/
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# Dynamic Integrity Measurement Architecture
|
||||
#
|
||||
config DIM
|
||||
bool "DIM (Dynamic Integrity Measurement)"
|
||||
depends on INTEGRITY
|
||||
default y
|
||||
help
|
||||
The Dynamic Integrity Measurement(DIM) architechture maintains a list
|
||||
of hash values of executables and running processes. If an attacker
|
||||
manages to change the executables being measured, we can tell.
|
||||
|
||||
If your system has a TPM chip, then DIM can extend the hash value to
|
||||
the TPM hardware, so that the TPM can prove to a third party whether
|
||||
or not critical executables have been modified.
|
||||
If unsure, say N.
|
||||
|
||||
if DIM
|
||||
|
||||
config DIM_CORE
|
||||
tristate "DIM core module support"
|
||||
default m
|
||||
help
|
||||
Enable the dim_core module of DIM architechture.
|
||||
|
||||
This option enables the dim_core security module, which provides
|
||||
integrity measurement for running processes. If unsure, say M.
|
||||
|
||||
config DIM_HASH_SUPPORT_SM3
|
||||
bool "Enable SM3 hash algorithm support for DIM"
|
||||
depends on DIM_CORE
|
||||
default y
|
||||
help
|
||||
Enable support for the SM3 hash algorithm in DIM.
|
||||
|
||||
If this option is enabled, the DIM module will be built with
|
||||
support for the SM3 hash algorithm. If unsure, say N.
|
||||
|
||||
config DIM_MONITOR
|
||||
tristate "DIM monitor module support"
|
||||
depends on DIM_CORE
|
||||
default m
|
||||
help
|
||||
Enable the dim_monitor module of DIM architechture.
|
||||
|
||||
This option enables the dim_monitor security module, which provides
|
||||
integrity measurement for dim_core. If unsure, say N.
|
||||
|
||||
endif # If DIM
|
|
@ -0,0 +1,79 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
# Makefile for DIM Linux Security Modules.
|
||||
#
|
||||
ifeq ($(CONFIG_DIM), y)
|
||||
|
||||
# DIM_CORE Module
|
||||
obj-$(CONFIG_DIM_CORE) += dim_core.o
|
||||
|
||||
dim_core-y += core/dim_core_main.o
|
||||
dim_core-y += core/dim_core_fs.o
|
||||
dim_core-y += core/dim_core_mem_pool.o
|
||||
dim_core-y += core/dim_core_measure.o
|
||||
dim_core-y += core/dim_core_symbol.o
|
||||
dim_core-y += core/dim_core_sig.o
|
||||
|
||||
dim_core-y += core/tasks/dim_core_measure_kernel.o
|
||||
dim_core-y += core/tasks/dim_core_measure_module.o
|
||||
dim_core-y += core/tasks/dim_core_measure_process/dim_vm_hash.o
|
||||
dim_core-y += core/tasks/dim_core_measure_process/dim_core_measure_process.o
|
||||
dim_core-y += core/tasks/dim_core_measure_process/dim_core_measure_process_vma.o
|
||||
|
||||
dim_core-y += core/policy/dim_core_policy.o
|
||||
dim_core-y += core/policy/dim_core_policy_complex.o
|
||||
|
||||
dim_core-y += core/static_baseline/dim_core_static_baseline.o
|
||||
dim_core-y += core/static_baseline/dim_core_static_baseline_complex.o
|
||||
|
||||
dim_core-y += common/dim_entry.o
|
||||
dim_core-y += common/dim_utils.o
|
||||
dim_core-y += common/dim_baseline.o
|
||||
dim_core-y += common/dim_hash.o
|
||||
dim_core-y += common/dim_measure_log.o
|
||||
dim_core-y += common/dim_tpm.o
|
||||
dim_core-y += common/dim_symbol.o
|
||||
dim_core-y += common/dim_safe_func.o
|
||||
dim_core-y += measure/dim_measure.o
|
||||
dim_core-y += measure/dim_measure_baseline.o
|
||||
dim_core-y += measure/dim_measure_task.o
|
||||
dim_core-y += measure/dim_measure_utils.o
|
||||
dim_core-y += measure/dim_measure_status.o
|
||||
|
||||
# DIM_MONITOR Module
|
||||
obj-$(CONFIG_DIM_MONITOR) += dim_monitor.o
|
||||
|
||||
dim_monitor-y += monitor/dim_monitor_main.o
|
||||
dim_monitor-y += monitor/dim_monitor_fs.o
|
||||
dim_monitor-y += monitor/dim_monitor_measure.o
|
||||
dim_monitor-y += monitor/dim_monitor_symbol.o
|
||||
|
||||
dim_monitor-y += monitor/measure_task/dim_monitor_measure_data.o
|
||||
dim_monitor-y += monitor/measure_task/dim_monitor_measure_text.o
|
||||
|
||||
dim_monitor-y += common/dim_entry.o
|
||||
dim_monitor-y += common/dim_hash.o
|
||||
dim_monitor-y += common/dim_utils.o
|
||||
dim_monitor-y += common/dim_measure_log.o
|
||||
dim_monitor-y += common/dim_baseline.o
|
||||
dim_monitor-y += common/dim_tpm.o
|
||||
dim_monitor-y += common/dim_symbol.o
|
||||
dim_monitor-y += common/dim_safe_func.o
|
||||
dim_monitor-y += measure/dim_measure.o
|
||||
dim_monitor-y += measure/dim_measure_baseline.o
|
||||
dim_monitor-y += measure/dim_measure_task.o
|
||||
dim_monitor-y += measure/dim_measure_utils.o
|
||||
dim_monitor-y += measure/dim_measure_status.o
|
||||
|
||||
subdir-ccflags-y := -I$(srctree)/$(src)/core
|
||||
subdir-ccflags-y += -I$(srctree)/$(src)/core/static_baseline
|
||||
subdir-ccflags-y += -I$(srctree)/$(src)/core/tasks
|
||||
subdir-ccflags-y += -I$(srctree)/$(src)/core/tasks/dim_core_measure_process
|
||||
subdir-ccflags-y += -I$(srctree)/$(src)/core/policy
|
||||
subdir-ccflags-y += -I$(srctree)/$(src)/monitor
|
||||
subdir-ccflags-y += -I$(srctree)/$(src)/monitor/measure_task
|
||||
subdir-ccflags-y += -I$(srctree)/$(src)/common
|
||||
subdir-ccflags-y += -I$(srctree)/$(src)/measure
|
||||
|
||||
endif
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dim_rb.h"
|
||||
#include "dim_baseline.h"
|
||||
#include "dim_utils.h"
|
||||
#include "dim_safe_func.h"
|
||||
|
||||
static int dim_baseline_compare(struct dim_baseline *x,
|
||||
struct dim_baseline *y)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (x->type != y->type)
|
||||
return x->type > y->type ? 1 : -1;
|
||||
|
||||
ret = dim_strcmp(x->name, y->name);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* in rb_tree search code, x is the find data, y is the target data,
|
||||
if the digest of y is zero, means we want to search digest by
|
||||
baseline name and type. */
|
||||
return dim_digest_is_zero(&y->digest) ? 0 :
|
||||
dim_digest_compare(&x->digest, &y->digest);
|
||||
}
|
||||
|
||||
/*
|
||||
static int dim_baseline_rb_find(struct rb_root *root,
|
||||
struct dim_baseline *data,
|
||||
struct dim_baseline **find_data)
|
||||
*/
|
||||
dim_rb_find(dim_baseline);
|
||||
/*
|
||||
static int dim_baseline_rb_add(struct rb_root *root,
|
||||
struct dim_baseline *data,
|
||||
struct dim_baseline **find_data)
|
||||
*/
|
||||
dim_rb_add(dim_baseline);
|
||||
|
||||
int dim_baseline_search_digest(struct dim_baseline_tree *root, const char *name,
|
||||
int type, struct dim_digest *digest)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_baseline *find = NULL;
|
||||
struct dim_baseline search = { .name = name, .type = type };
|
||||
|
||||
if (root == NULL || name == NULL || digest == NULL ||
|
||||
!dim_baseline_type_is_valid(type))
|
||||
return -EINVAL;
|
||||
|
||||
/* using zero digest means searching the digest */
|
||||
memset(&search.digest, 0, sizeof(struct dim_digest));
|
||||
|
||||
read_lock(&root->lock);
|
||||
ret = dim_baseline_rb_find(&root->rb_root, &search, &find);
|
||||
read_unlock(&root->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return dim_digest_copy(digest, &find->digest);
|
||||
}
|
||||
|
||||
bool dim_baseline_match(struct dim_baseline_tree *root, const char *name,
|
||||
int type, struct dim_digest *digest)
|
||||
{
|
||||
bool matched = false;
|
||||
struct dim_baseline search = { .name = name, .type = type };
|
||||
|
||||
if (root == NULL || name == NULL || digest == NULL ||
|
||||
!dim_baseline_type_is_valid(type) ||
|
||||
dim_digest_copy(&search.digest, digest) < 0)
|
||||
return false;
|
||||
|
||||
read_lock(&root->lock);
|
||||
matched = (dim_baseline_rb_find(&root->rb_root, &search, NULL) == 0);
|
||||
read_unlock(&root->lock);
|
||||
return matched;
|
||||
}
|
||||
|
||||
int dim_baseline_add(struct dim_baseline_tree *root, const char *name,
|
||||
int type, struct dim_digest *digest)
|
||||
{
|
||||
int ret = 0;
|
||||
int buf_len = 0;
|
||||
struct dim_baseline *baseline = NULL;
|
||||
|
||||
if (root == NULL || root->malloc == NULL || root->free == NULL ||
|
||||
!dim_baseline_type_is_valid(type) || name == NULL || digest == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
baseline = root->malloc(sizeof(struct dim_baseline));
|
||||
if (baseline == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
buf_len = strlen(name) + 1;
|
||||
baseline->name = root->malloc(buf_len);
|
||||
if (baseline->name == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
baseline->type = type;
|
||||
ret = dim_digest_copy(&baseline->digest, digest);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
strncpy((char *)baseline->name, name, buf_len - 1);
|
||||
((char *)baseline->name)[buf_len - 1] = '\0';
|
||||
|
||||
write_lock(&root->lock);
|
||||
ret = dim_baseline_rb_add(&root->rb_root, baseline, NULL);
|
||||
write_unlock(&root->lock);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
if (baseline->name != NULL)
|
||||
root->free((char *)baseline->name);
|
||||
|
||||
root->free(baseline);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dim_baseline_destroy_tree(struct dim_baseline_tree *root)
|
||||
{
|
||||
struct dim_baseline *pos = NULL;
|
||||
struct dim_baseline *n = NULL;
|
||||
|
||||
if (root == NULL || root->free == NULL)
|
||||
return;
|
||||
|
||||
write_lock(&root->lock);
|
||||
rbtree_postorder_for_each_entry_safe(pos, n, &root->rb_root, rb_node) {
|
||||
root->free((void *)pos->name);
|
||||
root->free(pos);
|
||||
}
|
||||
|
||||
root->rb_root = RB_ROOT;
|
||||
write_unlock(&root->lock);
|
||||
}
|
||||
|
||||
int dim_baseline_init_tree(malloc_func malloc, free_func free,
|
||||
struct dim_baseline_tree *root)
|
||||
{
|
||||
if (root == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
rwlock_init(&root->lock);
|
||||
root->rb_root = RB_ROOT;
|
||||
/* use kmalloc by default */
|
||||
root->malloc = malloc == NULL ? dim_kzalloc_gfp : malloc;
|
||||
root->free = free == NULL ? dim_kfree : free;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_BASELINE_H
|
||||
#define __DIM_BASELINE_H
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include "dim_hash.h"
|
||||
|
||||
typedef void *(*malloc_func)(size_t);
|
||||
typedef void (*free_func)(const void*);
|
||||
|
||||
enum dim_baseline_type {
|
||||
DIM_BASELINE_USER, /* baseline of user process */
|
||||
DIM_BASELINE_KERNEL, /* baseline of kernel or kernel modules */
|
||||
DIM_BASELINE_DATA,
|
||||
DIM_BASELINE_TRAMPOLINE,
|
||||
DIM_BASELINE_LAST,
|
||||
};
|
||||
|
||||
static const char *const dim_baseline_name[DIM_BASELINE_LAST] = {
|
||||
[DIM_BASELINE_USER] = "USER",
|
||||
[DIM_BASELINE_KERNEL] = "KERNEL",
|
||||
[DIM_BASELINE_DATA] = "DATA",
|
||||
[DIM_BASELINE_TRAMPOLINE] = "TRAMPOLINE",
|
||||
};
|
||||
|
||||
struct dim_baseline_tree {
|
||||
struct rb_root rb_root; /* rb tree of baseline nodes */
|
||||
rwlock_t lock;
|
||||
malloc_func malloc;
|
||||
free_func free;
|
||||
};
|
||||
|
||||
/* dim baseline node */
|
||||
struct dim_baseline {
|
||||
struct rb_node rb_node;
|
||||
const char *name;
|
||||
int type; /* enum dim_baseline_type */
|
||||
struct dim_digest digest;
|
||||
};
|
||||
|
||||
static inline bool dim_baseline_type_is_valid(int type)
|
||||
{
|
||||
return (type < DIM_BASELINE_LAST && type >= 0);
|
||||
}
|
||||
|
||||
static inline int dim_baseline_get_type(const char *name)
|
||||
{
|
||||
int idx = match_string(dim_baseline_name, DIM_BASELINE_LAST, name);
|
||||
return idx < 0 ? DIM_BASELINE_LAST : idx;
|
||||
}
|
||||
|
||||
int dim_baseline_init_tree(malloc_func malloc, free_func free,
|
||||
struct dim_baseline_tree *root);
|
||||
void dim_baseline_destroy_tree(struct dim_baseline_tree *root);
|
||||
int dim_baseline_search_digest(struct dim_baseline_tree *root, const char *name,
|
||||
int type, struct dim_digest *digest);
|
||||
bool dim_baseline_match(struct dim_baseline_tree *root, const char *name,
|
||||
int type, struct dim_digest *digest);
|
||||
int dim_baseline_add(struct dim_baseline_tree *root, const char *name,
|
||||
int type, struct dim_digest *digest);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "dim_entry.h"
|
||||
|
||||
#define WAIT_TIME_MAX 1000
|
||||
|
||||
int dim_entry_create(struct dim_entry *entry, struct dentry *parent)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (entry == NULL || entry->name == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
entry->dentry = securityfs_create_file(entry->name, entry->mode,
|
||||
parent, NULL, entry->fops);
|
||||
if (IS_ERR(entry->dentry)) {
|
||||
ret = PTR_ERR(entry->dentry);
|
||||
entry->dentry = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dim_entry_remove(struct dim_entry *entry)
|
||||
{
|
||||
int time_ms = 0;
|
||||
|
||||
if (entry != NULL && entry->dentry != NULL) {
|
||||
while (d_is_dir(entry->dentry) &&
|
||||
!simple_empty(entry->dentry) &&
|
||||
time_ms < WAIT_TIME_MAX) {
|
||||
time_ms++;
|
||||
msleep(1);
|
||||
}
|
||||
securityfs_remove(entry->dentry);
|
||||
entry->dentry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int dim_entry_create_list(struct dim_entry **list,
|
||||
unsigned int len,
|
||||
struct dentry *parent)
|
||||
{
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
|
||||
if (list == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = dim_entry_create(list[i], parent);
|
||||
if (ret < 0) {
|
||||
dim_entry_remove_list(list, len);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dim_entry_remove_list(struct dim_entry **list, unsigned int len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
dim_entry_remove(list[i]);
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_ENTRY_H
|
||||
#define __DIM_ENTRY_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include "dim_measure_log.h"
|
||||
|
||||
#define DIM_ENTRY_DIR_MASK (S_IFDIR | S_IXUSR | S_IRUSR)
|
||||
#define DIM_ENTRY_RW_MASK (S_IWUSR | S_IRUSR)
|
||||
#define DIM_ENTRY_W_MASK (S_IWUSR)
|
||||
#define DIM_ENTRY_R_MASK (S_IRUSR)
|
||||
#define DIM_FS_TMP_BUF_SIZE 512
|
||||
|
||||
struct dim_entry {
|
||||
const char *name;
|
||||
umode_t mode;
|
||||
const struct file_operations *fops;
|
||||
struct dentry *dentry;
|
||||
};
|
||||
|
||||
/* the file interface for trigger by 'echo 1 > file_path' */
|
||||
#define dim_trigger_entry(sname, fname, function) \
|
||||
static ssize_t sname##_trigger(struct file *file, \
|
||||
const char __user *buf, \
|
||||
size_t count, loff_t *ppos) \
|
||||
{ \
|
||||
int val = 0; \
|
||||
int ret = 0; \
|
||||
\
|
||||
if (*ppos != 0 || count > 2) \
|
||||
return -EINVAL; \
|
||||
\
|
||||
ret = kstrtoint_from_user(buf, count, 10, &val); \
|
||||
if (ret < 0 || val != 1) \
|
||||
return ret < 0 ? ret : -EINVAL; \
|
||||
\
|
||||
ret = function(); \
|
||||
if (ret < 0) \
|
||||
return ret; \
|
||||
\
|
||||
return count; \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations sname##_ops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.write = sname##_trigger, \
|
||||
.llseek = generic_file_llseek, \
|
||||
}; \
|
||||
\
|
||||
static struct dim_entry sname##_entry = { \
|
||||
.name = #fname, \
|
||||
.mode = DIM_ENTRY_W_MASK, \
|
||||
.fops = &sname##_ops, \
|
||||
};
|
||||
|
||||
/* the file interface for reading measure log */
|
||||
#define dim_measure_log_entry(sname, fname, root_ptr) \
|
||||
static void *measure_log_read_start(struct seq_file *m, loff_t *pos) \
|
||||
{ \
|
||||
read_lock(&(root_ptr)->lock); \
|
||||
return seq_list_start(&(root_ptr)->list_root, *pos); \
|
||||
} \
|
||||
\
|
||||
static void *measure_log_read_next(struct seq_file *m, \
|
||||
void *v, loff_t *pos) \
|
||||
{ \
|
||||
return seq_list_next(v, &(root_ptr)->list_root, pos); \
|
||||
} \
|
||||
\
|
||||
static void measure_log_read_stop(struct seq_file *m, void *v) \
|
||||
{ \
|
||||
read_unlock(&(root_ptr)->lock); \
|
||||
} \
|
||||
\
|
||||
static int measure_log_read_show(struct seq_file *m, void *v) \
|
||||
{ \
|
||||
struct dim_measure_log *log = \
|
||||
list_entry(v, struct dim_measure_log, node_order); \
|
||||
\
|
||||
return dim_measure_log_seq_show(m, log); \
|
||||
} \
|
||||
\
|
||||
const struct seq_operations sname##_seqops = { \
|
||||
.start = measure_log_read_start, \
|
||||
.next = measure_log_read_next, \
|
||||
.stop = measure_log_read_stop, \
|
||||
.show = measure_log_read_show, \
|
||||
}; \
|
||||
\
|
||||
static int sname##_open(struct inode *inode, struct file *file) \
|
||||
{ \
|
||||
return seq_open(file, &sname##_seqops); \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations sname##_ops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.open = sname##_open, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = seq_release, \
|
||||
}; \
|
||||
\
|
||||
static struct dim_entry sname##_entry = { \
|
||||
.name = #fname, \
|
||||
.mode = DIM_ENTRY_R_MASK, \
|
||||
.fops = &sname##_ops, \
|
||||
};
|
||||
|
||||
/* the file interface for print string */
|
||||
#define dim_string_print_entry(sname, fname, function) \
|
||||
static ssize_t sname##_read(struct file *file, \
|
||||
char __user *buf, \
|
||||
size_t count, loff_t *ppos) \
|
||||
{ \
|
||||
char tmpbuf[DIM_FS_TMP_BUF_SIZE]; \
|
||||
ssize_t len; \
|
||||
\
|
||||
len = scnprintf(tmpbuf, \
|
||||
DIM_FS_TMP_BUF_SIZE, \
|
||||
"%s\n", \
|
||||
function()); \
|
||||
\
|
||||
return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations sname##_ops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.read = sname##_read, \
|
||||
.llseek = generic_file_llseek, \
|
||||
}; \
|
||||
\
|
||||
static struct dim_entry sname##_entry = { \
|
||||
.name = #fname, \
|
||||
.mode = DIM_ENTRY_R_MASK, \
|
||||
.fops = &sname##_ops, \
|
||||
};
|
||||
|
||||
/* the file interface for reading and writing uint parameter */
|
||||
#define dim_uint_rw_entry(sname, fname, read_func, write_func) \
|
||||
static ssize_t sname##_read(struct file *file, \
|
||||
char __user *buf, \
|
||||
size_t count, loff_t *ppos) \
|
||||
{ \
|
||||
long len = 0; \
|
||||
long val = 0; \
|
||||
char tmpbuf[DIM_FS_TMP_BUF_SIZE]; \
|
||||
\
|
||||
val = read_func(); \
|
||||
if (val < 0) \
|
||||
return val; \
|
||||
\
|
||||
len = scnprintf(tmpbuf, DIM_FS_TMP_BUF_SIZE, "%ld\n", val); \
|
||||
return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); \
|
||||
} \
|
||||
\
|
||||
static ssize_t sname##_write(struct file *file, \
|
||||
const char __user *buf, \
|
||||
size_t count, loff_t *ppos) \
|
||||
{ \
|
||||
int ret = 0; \
|
||||
unsigned int val; \
|
||||
\
|
||||
ret = kstrtouint_from_user(buf, count, 10, &val); \
|
||||
if (ret < 0) \
|
||||
return -EINVAL; \
|
||||
\
|
||||
ret = write_func(val); \
|
||||
return ret < 0 ? ret : count; \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations sname##_ops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.read = sname##_read, \
|
||||
.write = sname##_write, \
|
||||
.llseek = generic_file_llseek, \
|
||||
}; \
|
||||
\
|
||||
static struct dim_entry sname##_entry = { \
|
||||
.name = #fname, \
|
||||
.mode = DIM_ENTRY_RW_MASK, \
|
||||
.fops = &sname##_ops, \
|
||||
};
|
||||
|
||||
int dim_entry_create(struct dim_entry *entry, struct dentry *parent);
|
||||
void dim_entry_remove(struct dim_entry *entry);
|
||||
int dim_entry_create_list(struct dim_entry **list,
|
||||
unsigned int len,
|
||||
struct dentry *parent);
|
||||
void dim_entry_remove_list(struct dim_entry **list, unsigned int len);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "dim_hash.h"
|
||||
#include "dim_utils.h"
|
||||
|
||||
static const char *allow_hash[] = {
|
||||
"sha256",
|
||||
#ifdef CONFIG_DIM_HASH_SUPPORT_SM3
|
||||
"sm3",
|
||||
#endif
|
||||
};
|
||||
|
||||
int dim_hash_init(const char *algo_name, struct dim_hash *hash)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (algo_name == NULL || hash == NULL ||
|
||||
match_string(allow_hash, DIM_ARRAY_LEN(allow_hash), algo_name) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
hash->algo = dim_hash_algo(algo_name);
|
||||
if (hash->algo == HASH_ALGO__LAST)
|
||||
return -EINVAL;
|
||||
|
||||
hash->tfm = crypto_alloc_shash(algo_name, 0, 0);
|
||||
if (IS_ERR(hash->tfm)) {
|
||||
ret = PTR_ERR(hash->tfm);
|
||||
hash->tfm = NULL;
|
||||
}
|
||||
|
||||
hash->name = algo_name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dim_hash_destroy(struct dim_hash *hash)
|
||||
{
|
||||
if (hash == NULL)
|
||||
return;
|
||||
|
||||
crypto_free_shash(hash->tfm);
|
||||
hash->tfm = NULL;
|
||||
}
|
||||
|
||||
int dim_hash_calculate(const void *data, unsigned int len,
|
||||
struct dim_hash *alg,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
int ret = 0;
|
||||
SHASH_DESC_ON_STACK(shash, alg->tfm);
|
||||
|
||||
if (data == NULL || alg == NULL || digest == NULL || alg->tfm == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
digest->algo = alg->algo;
|
||||
shash->tfm = alg->tfm;
|
||||
ret = crypto_shash_init(shash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = crypto_shash_update(shash, data, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return crypto_shash_final(shash, digest->data);
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_HASH_H
|
||||
#define __DIM_HASH_H
|
||||
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
#define DIM_MAX_DIGEST_SIZE 32 /* now is the size of SHA256 */
|
||||
|
||||
struct dim_hash {
|
||||
const char *name; /* algorithm name */
|
||||
struct crypto_shash *tfm;
|
||||
int algo; /* enum hash_algo */
|
||||
};
|
||||
|
||||
struct dim_digest {
|
||||
int algo; /* enum hash_algo */
|
||||
char data[DIM_MAX_DIGEST_SIZE];
|
||||
};
|
||||
|
||||
static inline int dim_hash_algo(const char *name)
|
||||
{
|
||||
int idx = match_string(hash_algo_name, HASH_ALGO__LAST, name);
|
||||
return idx < 0 ? HASH_ALGO__LAST : idx;
|
||||
}
|
||||
|
||||
static inline const char *dim_hash_name(int algo)
|
||||
{
|
||||
return (algo < 0 || algo >= HASH_ALGO__LAST) ?
|
||||
NULL : hash_algo_name[algo];
|
||||
}
|
||||
|
||||
static inline int dim_digest_size(int algo)
|
||||
{
|
||||
return (algo < 0 || algo >= HASH_ALGO__LAST) ?
|
||||
0 : hash_digest_size[algo];
|
||||
}
|
||||
|
||||
static inline bool dim_digest_is_zero(struct dim_digest *digest)
|
||||
{
|
||||
struct dim_digest z_digest = { 0 };
|
||||
return memcmp(&z_digest, digest, sizeof(struct dim_digest)) == 0;
|
||||
}
|
||||
|
||||
static inline int dim_digest_compare(struct dim_digest *x,
|
||||
struct dim_digest *y)
|
||||
{
|
||||
if (x->algo != y->algo)
|
||||
return x->algo > y->algo ? 1 : -1;
|
||||
|
||||
return memcmp(x->data, y->data, dim_digest_size(x->algo));
|
||||
}
|
||||
|
||||
static inline int dim_digest_copy(struct dim_digest *dst,
|
||||
struct dim_digest *src)
|
||||
{
|
||||
dst->algo = src->algo;
|
||||
memcpy(dst->data, src->data, dim_digest_size(dst->algo));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dim_hash_init(const char *algo_name, struct dim_hash *hash);
|
||||
void dim_hash_destroy(struct dim_hash *hash);
|
||||
int dim_hash_calculate(const void *data, unsigned int len,
|
||||
struct dim_hash *alg,
|
||||
struct dim_digest *digest);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include "dim_rb.h"
|
||||
#include "dim_tpm.h"
|
||||
#include "dim_safe_func.h"
|
||||
#include "dim_measure_log.h"
|
||||
|
||||
/*
|
||||
static int dim_measure_name_rb_add(struct rb_root *root,
|
||||
struct dim_baseline *data,
|
||||
struct dim_baseline **find_data)
|
||||
*/
|
||||
dim_rb_add(dim_measure_name);
|
||||
|
||||
static int cal_measure_log_digest(const char *name,
|
||||
struct dim_measure_log *info,
|
||||
struct dim_hash *hash)
|
||||
{
|
||||
/* templet hash is hash(
|
||||
"file hash algorithm string size + file digest size"
|
||||
+ "file hash algorithm string"
|
||||
+ "file digest"
|
||||
+ "file path string size"
|
||||
+ "file path"), and the "\0" of algorithm/path string should
|
||||
be calculated in also.
|
||||
*/
|
||||
int ret, size;
|
||||
const char *algo_name = dim_hash_name(info->digest.algo);
|
||||
int digest_size = dim_digest_size(info->digest.algo);
|
||||
SHASH_DESC_ON_STACK(shash, hash->tfm);
|
||||
|
||||
if (algo_name == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
shash->tfm = hash->tfm;
|
||||
ret = crypto_shash_init(shash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
size = strlen(algo_name) + strlen(":") + 1 + digest_size;
|
||||
if ((ret = crypto_shash_update(shash, (char *)&size, sizeof(size))) ||
|
||||
(ret = crypto_shash_update(shash, algo_name, strlen(algo_name))) ||
|
||||
(ret = crypto_shash_update(shash, ":", strlen(":") + 1)) ||
|
||||
(ret = crypto_shash_update(shash, info->digest.data, digest_size)))
|
||||
return ret;
|
||||
|
||||
size = strlen(name) + 1;
|
||||
if ((ret = crypto_shash_update(shash, (char *)&size, sizeof(size))) ||
|
||||
(ret = crypto_shash_update(shash, name, size))) /* + "\0" */
|
||||
return ret;
|
||||
|
||||
info->log_digest.algo = hash->algo;
|
||||
return crypto_shash_final(shash, info->log_digest.data);
|
||||
}
|
||||
|
||||
int dim_measure_log_seq_show(struct seq_file *m, struct dim_measure_log *info)
|
||||
{
|
||||
char log_digest_buf[(DIM_MAX_DIGEST_SIZE << 1) + 1] = { 0 };
|
||||
char digest_buf[(DIM_MAX_DIGEST_SIZE << 1) + 1] = { 0 };
|
||||
|
||||
bin2hex(log_digest_buf, info->log_digest.data,
|
||||
dim_digest_size(info->log_digest.algo));
|
||||
|
||||
bin2hex(digest_buf, info->digest.data,
|
||||
dim_digest_size(info->digest.algo));
|
||||
|
||||
seq_printf(m, "%d %s %s:%s %s %s\n",
|
||||
info->pcr,
|
||||
log_digest_buf,
|
||||
dim_hash_name(info->digest.algo),
|
||||
digest_buf,
|
||||
dim_measure_log_name(info),
|
||||
dim_measure_log_type_to_name(info->type));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int measure_info_insert(struct dim_measure_name *name,
|
||||
struct dim_measure_log *info)
|
||||
{
|
||||
struct list_head *list_search_from = NULL;
|
||||
struct dim_measure_log *pos = NULL;
|
||||
int cnt = 0;
|
||||
|
||||
/* For error type log, search from last baseline */
|
||||
list_search_from = info->type == LOG_TAMPERED ?
|
||||
name->log_cur->next : name->log_root.next;
|
||||
|
||||
pos = list_entry(list_search_from, struct dim_measure_log, node);
|
||||
list_for_each_entry_from(pos, &name->log_root, node) {
|
||||
if (is_same_dim_measure_log(pos, info))
|
||||
return -EEXIST;
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
if (cnt > LOG_NUMBER_FILE_MAX && info->type == LOG_TAMPERED)
|
||||
return -ENOSPC;
|
||||
|
||||
list_add_tail(&info->node, &name->log_root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void measure_log_destroy_info(struct dim_measure_log *info)
|
||||
{
|
||||
dim_kfree(info);
|
||||
}
|
||||
|
||||
static void measure_log_destroy_name(struct dim_measure_name *name)
|
||||
{
|
||||
struct dim_measure_log *pos = NULL;
|
||||
struct dim_measure_log *n = NULL;
|
||||
|
||||
/* free all measure info */
|
||||
list_for_each_entry_safe(pos, n, &name->log_root, node)
|
||||
measure_log_destroy_info(pos);
|
||||
/* free self */
|
||||
dim_kfree(name->name);
|
||||
dim_kfree(name);
|
||||
}
|
||||
|
||||
static int measure_log_create_name(const char *name_str,
|
||||
struct dim_measure_name **name)
|
||||
{
|
||||
struct dim_measure_name *new = NULL;
|
||||
|
||||
new = dim_kzalloc_gfp(sizeof(struct dim_measure_name));
|
||||
if (new == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
new->name = dim_kstrdup_gfp(name_str);
|
||||
if (new->name == NULL) {
|
||||
dim_kfree(new);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&new->log_root);
|
||||
new->log_cur = &new->log_root;
|
||||
*name = new;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int measure_log_create_info(char pcr, struct dim_digest *digest,
|
||||
int flag, struct dim_measure_log **info)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_measure_log *new = NULL;
|
||||
|
||||
new = dim_kzalloc_gfp(sizeof(struct dim_measure_log));
|
||||
if (new == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
new->pcr = pcr;
|
||||
new->type = flag;
|
||||
ret = dim_digest_copy(&new->digest, digest);
|
||||
if (ret < 0) {
|
||||
dim_kfree(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*info = new;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int measure_log_add_info(struct dim_measure_log_tree *root,
|
||||
const char *name_str,
|
||||
struct dim_measure_log *info)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_measure_name *name = NULL;
|
||||
struct dim_measure_name *name_find = NULL;
|
||||
|
||||
ret = cal_measure_log_digest(name_str, info, root->hash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = measure_log_create_name(name_str, &name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
write_lock(&root->lock);
|
||||
ret = dim_measure_name_rb_add(&root->rb_root, name, &name_find);
|
||||
if (ret == -EEXIST && name_find != NULL) { /* name node exist */
|
||||
measure_log_destroy_name(name);
|
||||
info->name_head = name_find;
|
||||
} else if (ret < 0) { /* unknown error */
|
||||
measure_log_destroy_name(name);
|
||||
write_unlock(&root->lock);
|
||||
return ret;
|
||||
} else { /* name node insert ok */
|
||||
info->name_head = name;
|
||||
}
|
||||
|
||||
ret = measure_info_insert(info->name_head, info);
|
||||
if (ret < 0) {
|
||||
write_unlock(&root->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_add_tail(&info->node_order, &root->list_root);
|
||||
root->count++;
|
||||
write_unlock(&root->lock);
|
||||
|
||||
return root->tpm == NULL && root->pcr != 0 ? 0 :
|
||||
dim_tpm_pcr_extend(root->tpm, root->pcr,
|
||||
&info->log_digest);
|
||||
}
|
||||
|
||||
static bool measure_log_is_full(struct dim_measure_log_tree *root)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
read_lock(&root->lock);
|
||||
ret = ((root->cap > 0) && (root->count >= root->cap));
|
||||
read_unlock(&root->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dim_measure_log_add(struct dim_measure_log_tree *root,
|
||||
const char *name_str,
|
||||
struct dim_digest *digest, int flag)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_measure_log *info = NULL;
|
||||
|
||||
if (root == NULL || name_str == NULL ||
|
||||
!is_valid_dim_measure_log_type(flag) || digest == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (measure_log_is_full(root))
|
||||
return -ENOSPC;
|
||||
|
||||
ret = measure_log_create_info(root->pcr, digest, flag, &info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = measure_log_add_info(root, name_str, info);
|
||||
if (ret < 0)
|
||||
measure_log_destroy_info(info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dim_measure_log_refresh(struct dim_measure_log_tree *root)
|
||||
{
|
||||
struct dim_measure_name *pos = NULL;
|
||||
struct dim_measure_name *n = NULL;
|
||||
|
||||
write_lock(&root->lock);
|
||||
rbtree_postorder_for_each_entry_safe(pos, n, &root->rb_root, rb_node)
|
||||
pos->log_cur = pos->log_root.prev;
|
||||
|
||||
write_unlock(&root->lock);
|
||||
}
|
||||
|
||||
void dim_measure_log_destroy_tree(struct dim_measure_log_tree *root)
|
||||
{
|
||||
struct dim_measure_name *pos = NULL;
|
||||
struct dim_measure_name *n = NULL;
|
||||
|
||||
write_lock(&root->lock);
|
||||
rbtree_postorder_for_each_entry_safe(pos, n, &root->rb_root, rb_node)
|
||||
measure_log_destroy_name(pos);
|
||||
|
||||
INIT_LIST_HEAD(&root->list_root);
|
||||
root->hash = NULL;
|
||||
root->rb_root = RB_ROOT;
|
||||
write_unlock(&root->lock);
|
||||
}
|
||||
|
||||
int dim_measure_log_init_tree(struct dim_measure_log_tree *root,
|
||||
struct dim_hash *hash,
|
||||
struct dim_tpm *tpm,
|
||||
unsigned int cap,
|
||||
unsigned int pcr)
|
||||
{
|
||||
if (root == NULL || hash == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
rwlock_init(&root->lock);
|
||||
INIT_LIST_HEAD(&root->list_root);
|
||||
root->hash = hash;
|
||||
root->rb_root = RB_ROOT;
|
||||
root->pcr = pcr;
|
||||
root->tpm = tpm;
|
||||
root->cap = cap;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_MEASURE_LOG_H
|
||||
#define __DIM_MEASURE_LOG_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
#include "dim_hash.h"
|
||||
#include "dim_safe_func.h"
|
||||
|
||||
#define DIM_NG "dim-ng"
|
||||
#define LOG_MAX_LENGTH_PCR 3
|
||||
#define LOG_NUMBER_FILE_MAX 10
|
||||
|
||||
enum dim_measure_log_type {
|
||||
LOG_NO_SATIC_BASELINE,
|
||||
LOG_STATIC_BASELINE,
|
||||
LOG_DYNAMIC_BASELINE,
|
||||
LOG_TAMPERED,
|
||||
LOG_MATCHED,
|
||||
LOG_LAST,
|
||||
};
|
||||
|
||||
static const char *dim_measure_log_type_name[LOG_LAST] = {
|
||||
[LOG_NO_SATIC_BASELINE] = "[no static baseline]",
|
||||
[LOG_STATIC_BASELINE] = "[static baseline]",
|
||||
[LOG_DYNAMIC_BASELINE] = "[dynamic baseline]",
|
||||
[LOG_TAMPERED] = "[tampered]",
|
||||
[LOG_MATCHED] = "[matched]",
|
||||
};
|
||||
|
||||
struct dim_measure_log_tree {
|
||||
struct rb_root rb_root; /* rb tree root for searching measure log */
|
||||
struct list_head list_root; /* list root for printing logs in order */
|
||||
struct dim_hash *hash; /* algorithm for calculating log hash */
|
||||
struct dim_tpm *tpm;
|
||||
unsigned int pcr;
|
||||
rwlock_t lock;
|
||||
unsigned int count; /* number of log */
|
||||
unsigned int cap; /* capacity of log */
|
||||
};
|
||||
|
||||
struct dim_measure_name {
|
||||
struct rb_node rb_node;
|
||||
struct list_head log_root; /* total dim_measure_log list */
|
||||
struct list_head *log_cur; /* current dim_measure_log list */
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct dim_measure_log {
|
||||
struct list_head node;
|
||||
struct list_head node_order;
|
||||
struct dim_measure_name *name_head;
|
||||
char pcr;
|
||||
int type; /* enum log_type */
|
||||
struct dim_digest digest; /* measure digest */
|
||||
struct dim_digest log_digest; /* measure log digest */
|
||||
};
|
||||
|
||||
static inline int dim_measure_name_compare(struct dim_measure_name *x,
|
||||
struct dim_measure_name *y)
|
||||
{
|
||||
return dim_strcmp(x->name, y->name);
|
||||
}
|
||||
|
||||
static inline const char *dim_measure_log_type_to_name(int type)
|
||||
{
|
||||
return (type < 0 || type >= LOG_LAST) ? NULL :
|
||||
dim_measure_log_type_name[type];
|
||||
}
|
||||
|
||||
static inline const char *dim_measure_log_name(struct dim_measure_log *log)
|
||||
{
|
||||
return log->name_head->name;
|
||||
}
|
||||
|
||||
static inline bool is_valid_dim_measure_log_type(int type)
|
||||
{
|
||||
return type < LOG_LAST && type >= 0;
|
||||
}
|
||||
|
||||
static inline bool is_same_dim_measure_log(struct dim_measure_log *x,
|
||||
struct dim_measure_log *y)
|
||||
{
|
||||
if (x->type != y->type)
|
||||
return false;
|
||||
|
||||
return dim_digest_compare(&x->digest, &y->digest) == 0;
|
||||
}
|
||||
|
||||
int dim_measure_log_init_tree(struct dim_measure_log_tree *root,
|
||||
struct dim_hash *hash, struct dim_tpm *tpm,
|
||||
unsigned int cap, unsigned int pcr);
|
||||
void dim_measure_log_destroy_tree(struct dim_measure_log_tree *root);
|
||||
int dim_measure_log_add(struct dim_measure_log_tree *root,
|
||||
const char *name_str,
|
||||
struct dim_digest *digest, int flag);
|
||||
int dim_measure_log_seq_show(struct seq_file *m, struct dim_measure_log *log);
|
||||
void dim_measure_log_refresh(struct dim_measure_log_tree *root);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_RB_H
|
||||
#define __DIM_RB_H
|
||||
|
||||
#define dim_rb_find(name) \
|
||||
static int name##_rb_find(struct rb_root *root, struct name *data, \
|
||||
struct name **find_data) \
|
||||
{ \
|
||||
int ret = 0; \
|
||||
struct rb_node *cur = root->rb_node; \
|
||||
struct name *find = NULL; \
|
||||
\
|
||||
while (cur != NULL) { \
|
||||
find = rb_entry(cur, struct name, rb_node); \
|
||||
ret = name##_compare(find, data); \
|
||||
if (ret == 0) { \
|
||||
if (find_data != NULL) \
|
||||
*find_data = find; \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
cur = ret < 0 ? cur->rb_left : cur->rb_right; \
|
||||
} \
|
||||
\
|
||||
return -ENOENT; \
|
||||
};
|
||||
|
||||
#define dim_rb_add(name) \
|
||||
static int name##_rb_add(struct rb_root *root, struct name *data, \
|
||||
struct name **find_data) \
|
||||
{ \
|
||||
int ret = 0; \
|
||||
struct rb_node **cur = &(root->rb_node); \
|
||||
struct rb_node *parent = NULL; \
|
||||
struct name *find = NULL; \
|
||||
\
|
||||
while (*cur != NULL) { \
|
||||
find = rb_entry(*cur, struct name, rb_node); \
|
||||
ret = name##_compare(find, data); \
|
||||
if (ret == 0) { \
|
||||
if (find_data != NULL) \
|
||||
*find_data = find; \
|
||||
return -EEXIST; \
|
||||
} \
|
||||
\
|
||||
parent = *cur; \
|
||||
cur = ret < 0 ? &(*cur)->rb_left : &(*cur)->rb_right; \
|
||||
} \
|
||||
\
|
||||
rb_link_node(&data->rb_node, parent, cur); \
|
||||
rb_insert_color(&data->rb_node, root); \
|
||||
return 0; \
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dim_safe_func.h"
|
||||
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
atomic_t dim_alloc_num = ATOMIC_INIT(0);;
|
||||
|
||||
void dim_check_memory_leak(void)
|
||||
{
|
||||
unsigned int n = atomic_read(&dim_alloc_num);
|
||||
if (n != 0)
|
||||
dim_warn("warning: detect %u memory leakage\n", n);
|
||||
else
|
||||
dim_info("not detect memory leakage\n");
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_SAFE_FUNC_H
|
||||
#define __DIM_SAFE_FUNC_H
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "dim_utils.h"
|
||||
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
extern atomic_t dim_alloc_num;
|
||||
|
||||
static inline void dim_alloc_debug_inc(void)
|
||||
{
|
||||
atomic_inc(&dim_alloc_num);
|
||||
}
|
||||
|
||||
static inline void dim_alloc_debug_dec(void)
|
||||
{
|
||||
atomic_dec(&dim_alloc_num);
|
||||
}
|
||||
|
||||
static inline void dim_print_alloc_num(const char *s)
|
||||
{
|
||||
dim_info("%s: dim_alloc_num=%d\n", s, atomic_read(&dim_alloc_num));
|
||||
}
|
||||
|
||||
void dim_check_memory_leak(void);
|
||||
#endif
|
||||
|
||||
static inline void *dim_kzalloc_gfp(size_t size)
|
||||
{
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
void *data = kzalloc(size, GFP_KERNEL);
|
||||
if (data != NULL)
|
||||
dim_alloc_debug_inc();
|
||||
return data;
|
||||
#else
|
||||
return kzalloc(size, GFP_KERNEL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void *dim_kcalloc_gfp(size_t n, size_t size)
|
||||
{
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
void *data = kcalloc(n, size, GFP_KERNEL);
|
||||
if (data != NULL)
|
||||
dim_alloc_debug_inc();
|
||||
return data;
|
||||
#else
|
||||
return kcalloc(n, size, GFP_KERNEL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void *dim_krealloc_atom(const void *p, size_t new_size)
|
||||
{
|
||||
return krealloc(p, new_size, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static inline void *dim_kmemdup_gfp(const void *src, size_t len)
|
||||
{
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
void *data = kmemdup(src, len, GFP_KERNEL);
|
||||
if (data != NULL)
|
||||
dim_alloc_debug_inc();
|
||||
return data;
|
||||
#else
|
||||
return kmemdup(src, len, GFP_KERNEL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void dim_kfree(const void *objp)
|
||||
{
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
if (objp != NULL)
|
||||
dim_alloc_debug_dec();
|
||||
#endif
|
||||
kfree(objp);
|
||||
}
|
||||
|
||||
static inline void *dim_vzalloc(size_t size)
|
||||
{
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
void *data = vzalloc(size);
|
||||
if (data != NULL)
|
||||
dim_alloc_debug_inc();
|
||||
return data;
|
||||
#else
|
||||
return vzalloc(size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void dim_vfree(void *data)
|
||||
{
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
if (data != NULL)
|
||||
dim_alloc_debug_dec();
|
||||
#endif
|
||||
vfree(data);
|
||||
}
|
||||
|
||||
static inline char *dim_kstrdup_gfp(const char *s)
|
||||
{
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
void *data = kstrdup(s, GFP_KERNEL);
|
||||
if (data != NULL)
|
||||
dim_alloc_debug_inc();
|
||||
return data;
|
||||
#else
|
||||
return kstrdup(s, GFP_KERNEL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int dim_strcmp(const char *cs, const char *ct)
|
||||
{
|
||||
if (cs == NULL || ct == NULL)
|
||||
return -1;
|
||||
|
||||
return strcmp(cs, ct);
|
||||
}
|
||||
|
||||
static inline int dim_strncmp(const char *cs, const char *ct, size_t count)
|
||||
{
|
||||
if (cs == NULL || ct == NULL)
|
||||
return -1;
|
||||
|
||||
return strncmp(cs, ct, count);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
#include "dim_safe_func.h"
|
||||
#include "dim_symbol.h"
|
||||
|
||||
static int find_kernel_symbol(unsigned long addr,
|
||||
char *buf,
|
||||
size_t *offset,
|
||||
size_t *size)
|
||||
{
|
||||
char tmp[KSYM_SYMBOL_LEN] = { 0 };
|
||||
|
||||
sprint_symbol(tmp, addr);
|
||||
|
||||
memset(buf, 0, KSYM_NAME_LEN);
|
||||
return sscanf(tmp, "%127[^+]+%lx/%lx", buf, offset, size) == 3
|
||||
? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
DIM_SYMBOL_LOOKUP_FUNC dim_get_symbol_lookup_func(void)
|
||||
{
|
||||
unsigned long kaddr = (unsigned long)&sprint_symbol;
|
||||
unsigned long prev = kaddr - 1;
|
||||
unsigned long next = kaddr;
|
||||
size_t offset, size;
|
||||
int i, ret;
|
||||
char symbol_name[KSYM_NAME_LEN] = { 0 };
|
||||
|
||||
for (i = 0; i < DIM_TRY_COUNT; i++) {
|
||||
ret = find_kernel_symbol(kaddr, symbol_name, &offset, &size);
|
||||
if (ret < 0 || offset > size)
|
||||
break;
|
||||
|
||||
if (dim_strcmp(symbol_name, DIM_KALLSYMS_LOOKUP_NAME) == 0)
|
||||
return (DIM_SYMBOL_LOOKUP_FUNC)(kaddr - offset);
|
||||
|
||||
if (kaddr == next) {
|
||||
next = next + size - offset;
|
||||
kaddr = prev;
|
||||
} else {
|
||||
prev = prev - offset - 1;
|
||||
kaddr = next;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_SYMBOL_H
|
||||
#define __DIM_SYMBOL_H
|
||||
|
||||
#define DIM_KALLSYMS_LOOKUP_NAME "kallsyms_lookup_name"
|
||||
#define DIM_TRY_COUNT 100
|
||||
|
||||
typedef void* (*DIM_SYMBOL_LOOKUP_FUNC)(const char *);
|
||||
|
||||
DIM_SYMBOL_LOOKUP_FUNC dim_get_symbol_lookup_func(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#include "dim_safe_func.h"
|
||||
#include "dim_tpm.h"
|
||||
|
||||
int dim_tpm_init(struct dim_tpm *tpm, int algo)
|
||||
{
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
|
||||
tpm->chip = tpm_default_chip();
|
||||
if (tpm->chip == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
tpm->digests = dim_kcalloc_gfp(tpm->chip->nr_allocated_banks,
|
||||
sizeof(struct tpm_digest));
|
||||
if (tpm->digests == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tpm->bank = -1;
|
||||
for (i = 0; i < tpm->chip->nr_allocated_banks; i++) {
|
||||
tpm->digests[i].alg_id = tpm->chip->allocated_banks[i].alg_id;
|
||||
if (tpm->chip->allocated_banks[i].crypto_id == algo)
|
||||
tpm->bank = i;
|
||||
|
||||
memset(tpm->digests[i].digest, 0xff, TPM_MAX_DIGEST_SIZE);
|
||||
}
|
||||
|
||||
if (tpm->bank == -1) {
|
||||
ret = -ENOENT; /* fail to find matched TPM bank */
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
put_device(&tpm->chip->dev);
|
||||
if (tpm->digests != NULL) {
|
||||
dim_kfree(tpm->digests);
|
||||
tpm->digests = NULL;
|
||||
}
|
||||
|
||||
tpm->chip = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dim_tpm_pcr_extend(struct dim_tpm *tpm, int pcr, struct dim_digest *digest)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
if (tpm == NULL || digest == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (tpm->chip == NULL)
|
||||
return 0;
|
||||
|
||||
size = dim_digest_size(digest->algo);
|
||||
if (size == 0 || size > TPM_MAX_DIGEST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(tpm->digests[tpm->bank].digest, digest->data, size);
|
||||
return tpm_pcr_extend(tpm->chip, pcr, tpm->digests);
|
||||
}
|
||||
|
||||
void dim_tpm_destroy(struct dim_tpm *tpm)
|
||||
{
|
||||
if (tpm == NULL || tpm->chip == NULL)
|
||||
return;
|
||||
|
||||
put_device(&tpm->chip->dev);
|
||||
dim_kfree(tpm->digests);
|
||||
tpm->chip = NULL;
|
||||
tpm->digests = NULL;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_TPM_H
|
||||
#define __DIM_TPM_H
|
||||
|
||||
#include <linux/tpm.h>
|
||||
#include "dim_hash.h"
|
||||
|
||||
#define DIM_PCR_MAX 128
|
||||
|
||||
struct dim_tpm {
|
||||
struct tpm_chip *chip;
|
||||
struct tpm_digest *digests;
|
||||
int bank;
|
||||
};
|
||||
|
||||
int dim_tpm_init(struct dim_tpm *tpm, int algo);
|
||||
int dim_tpm_pcr_extend(struct dim_tpm *tpm, int pcr, struct dim_digest *digest);
|
||||
void dim_tpm_destroy(struct dim_tpm *tpm);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include "dim_safe_func.h"
|
||||
#include "dim_utils.h"
|
||||
|
||||
#define DIM_MAX_LINE_BUF (8 * 1024)
|
||||
|
||||
int dim_get_absolute_path(const char *path, const char **result)
|
||||
{
|
||||
int ret = 0;
|
||||
struct path p;
|
||||
char *buf = NULL;
|
||||
char *apath = NULL;
|
||||
|
||||
if (path == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kern_path(path, LOOKUP_FOLLOW, &p);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
buf = dim_kzalloc_gfp(PATH_MAX);
|
||||
if (buf == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
apath = d_path(&p, buf, PATH_MAX);
|
||||
if (IS_ERR(apath)) {
|
||||
ret = PTR_ERR(apath);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*result = dim_kstrdup_gfp(apath);
|
||||
if (*result == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
path_put(&p);
|
||||
dim_kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dim_parse_line_buf(char *buf, loff_t len, int (*line_parser)(char *, int, void *), void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
int line_no = 1;
|
||||
char *line = buf;
|
||||
char *line_buf = NULL;
|
||||
size_t line_len = 0;
|
||||
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (buf[i] != '\n' && i != len - 1)
|
||||
continue;
|
||||
|
||||
if (buf[i] == '\n') {
|
||||
buf[i] = '\0';
|
||||
ret = line_parser(line, line_no, data);
|
||||
line = &buf[i + 1];
|
||||
} else {
|
||||
line_len = buf + i - line + 1;
|
||||
if (line_len + 1 > DIM_MAX_LINE_BUF) {
|
||||
dim_err("failed to alloc memory for line buff\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
line_buf = dim_kzalloc_gfp(line_len + 1);
|
||||
if (line_buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(line_buf, line, line_len);
|
||||
ret = line_parser(line_buf, line_no, data);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* if the parser returns -E2BIG, means the line number
|
||||
* is too large, the excess lines will be ignored.
|
||||
*/
|
||||
ret = (ret == -E2BIG) ? 0 : ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
line_no++;
|
||||
}
|
||||
out:
|
||||
if (line_buf != NULL)
|
||||
dim_kfree(line_buf);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_UTILS_H
|
||||
#define __DIM_UTILS_H
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/path.h>
|
||||
|
||||
#define DIM_ARRAY_LEN(ARR) (sizeof(ARR) / sizeof(ARR[0]))
|
||||
|
||||
#define dim_fmt(fmt) "%s: " fmt
|
||||
|
||||
#define dim_err(fmt, ...) pr_err(dim_fmt(fmt), THIS_MODULE->name, ##__VA_ARGS__)
|
||||
#define dim_warn(fmt, ...) pr_warn(dim_fmt(fmt), THIS_MODULE->name, ##__VA_ARGS__)
|
||||
#define dim_info(fmt, ...) pr_info(dim_fmt(fmt), THIS_MODULE->name, ##__VA_ARGS__)
|
||||
#define dim_devel(fmt, ...)
|
||||
|
||||
int dim_get_absolute_path(const char *path, const char **result);
|
||||
int dim_parse_line_buf(char *buf, loff_t len, int (*line_parser)(char *, int, void *), void *data);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "dim_utils.h"
|
||||
|
||||
#include "dim_core_measure.h"
|
||||
#include "dim_core_fs.h"
|
||||
|
||||
/*
|
||||
* measure trigger interface
|
||||
* dim_entry struct: dim_measure_entry
|
||||
* file entry name: measure
|
||||
* function: dim_core_measure_blocking()
|
||||
*/
|
||||
dim_trigger_entry(dim_measure, measure, dim_core_measure_blocking);
|
||||
|
||||
/*
|
||||
* baseline_init trigger interface
|
||||
* dim_entry struct: dim_baseline_init_entry
|
||||
* file entry name: baseline_init
|
||||
* function: dim_core_baseline_blocking(0)
|
||||
*/
|
||||
dim_trigger_entry(dim_baseline_init, baseline_init,
|
||||
dim_core_baseline_blocking);
|
||||
|
||||
/*
|
||||
* measure log read interface
|
||||
* dim_entry struct: dim_measure_log_entry
|
||||
* file entry name: runtime_status
|
||||
* status to read: dim_measure_log_tree
|
||||
*/
|
||||
dim_measure_log_entry(dim_measure_log, ascii_runtime_measurements,
|
||||
&dim_core_handle.log);
|
||||
|
||||
/*
|
||||
* status print interface
|
||||
* dim_entry struct: dim_status_entry
|
||||
* file entry name: runtime_status
|
||||
* print function: dim_core_status_print
|
||||
*/
|
||||
dim_string_print_entry(dim_status, runtime_status, dim_core_status_print);
|
||||
|
||||
/*
|
||||
* measure interval set and read interface
|
||||
* dim_entry struct: dim_interval_entry
|
||||
* file entry name: interval
|
||||
* read function: dim_core_interval_get
|
||||
* write function: dim_core_interval_set
|
||||
*/
|
||||
dim_uint_rw_entry(dim_interval, interval, dim_core_interval_get,
|
||||
dim_core_interval_set);
|
||||
|
||||
/*
|
||||
* measure action set and read interface
|
||||
* dim_entry struct: dim_tampered_action_entry
|
||||
* file entry name: tampered_action
|
||||
* read function: dim_core_measure_action_get
|
||||
* write function: dim_core_measure_action_set
|
||||
*/
|
||||
dim_uint_rw_entry(dim_tampered_action, tampered_action,
|
||||
dim_core_measure_action_get, dim_core_measure_action_set);
|
||||
|
||||
/*
|
||||
* dim directory
|
||||
*/
|
||||
static struct dim_entry dim_core_dir = {
|
||||
.name = "dim",
|
||||
.mode = DIM_ENTRY_DIR_MASK,
|
||||
.fops = NULL,
|
||||
.dentry = NULL,
|
||||
};
|
||||
|
||||
static struct dim_entry *dim_core_files[] = {
|
||||
&dim_measure_entry,
|
||||
&dim_baseline_init_entry,
|
||||
&dim_measure_log_entry,
|
||||
&dim_status_entry,
|
||||
&dim_interval_entry,
|
||||
&dim_tampered_action_entry,
|
||||
};
|
||||
|
||||
void dim_core_destroy_fs(void)
|
||||
{
|
||||
unsigned int len = DIM_ARRAY_LEN(dim_core_files);
|
||||
dim_entry_remove_list(dim_core_files, len);
|
||||
dim_entry_remove(&dim_core_dir);
|
||||
}
|
||||
|
||||
int dim_core_create_fs(void)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int len = DIM_ARRAY_LEN(dim_core_files);
|
||||
|
||||
ret = dim_entry_create(&dim_core_dir, NULL);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to create dim dir entry: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dim_entry_create_list(dim_core_files, len, dim_core_dir.dentry);
|
||||
if (ret < 0)
|
||||
dim_entry_remove(&dim_core_dir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct dim_entry *dim_root_entry(void)
|
||||
{
|
||||
return &dim_core_dir;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dim_root_entry);
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_CORE_FS_H
|
||||
#define __DIM_CORE_FS_H
|
||||
|
||||
#include "dim_entry.h"
|
||||
|
||||
void dim_core_destroy_fs(void);
|
||||
int dim_core_create_fs(void);
|
||||
struct dim_entry *dim_root_entry(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "dim_safe_func.h"
|
||||
|
||||
#include "dim_core_policy.h"
|
||||
#include "dim_core_symbol.h"
|
||||
#include "dim_core_fs.h"
|
||||
#include "dim_core_measure.h"
|
||||
#include "dim_core_mem_pool.h"
|
||||
#include "dim_core_sig.h"
|
||||
|
||||
/* common measurement configuration */
|
||||
static struct dim_measure_cfg cfg = {
|
||||
.alg_name = DIM_CORE_HASH_DEFAULT,
|
||||
.log_cap = DIM_CORE_LOG_CAP_DEFAULT,
|
||||
};
|
||||
|
||||
module_param_named(measure_log_capacity, cfg.log_cap, uint, 0);
|
||||
MODULE_PARM_DESC(measure_log_capacity, "Max number of measure log");
|
||||
|
||||
module_param_named(measure_schedule, cfg.schedule_ms, uint, 0);
|
||||
MODULE_PARM_DESC(measure_schedule, "Schedule time (ms) for each measure object");
|
||||
|
||||
module_param_named(measure_hash, cfg.alg_name, charp, 0);
|
||||
MODULE_PARM_DESC(measure_hash, "Hash algorithm for measurement");
|
||||
|
||||
module_param_named(measure_pcr, cfg.pcr, uint, 0);
|
||||
MODULE_PARM_DESC(measure_pcr, "TPM PCR index to extend measure log");
|
||||
|
||||
/* special measurement configuration for dim_core */
|
||||
static unsigned int measure_interval = 0;
|
||||
static bool signature = false;
|
||||
|
||||
module_param(measure_interval, uint, 0);
|
||||
MODULE_PARM_DESC(measure_interval, "Interval time (min) for automatic measurement");
|
||||
|
||||
module_param(signature, bool, 0);
|
||||
MODULE_PARM_DESC(signature, "Require signature for policy and static baseline");
|
||||
|
||||
static int __init dim_core_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dim_core_kallsyms_init();
|
||||
if (ret < 0) {
|
||||
dim_err("failed to initialize dim kernel symbol: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = dim_mem_pool_init();
|
||||
if (ret < 0) {
|
||||
dim_err("failed to initialize dim memory pool: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (signature) {
|
||||
ret = dim_core_sig_init();
|
||||
if (ret < 0) {
|
||||
dim_err("failed to initialize dim signature: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dim_core_measure_init(&cfg, measure_interval);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to initialize dim measurement: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = dim_core_create_fs();
|
||||
if (ret < 0) {
|
||||
dim_err("failed to create dim fs entry: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dim_core_destroy_fs();
|
||||
dim_core_measure_destroy();
|
||||
dim_mem_pool_destroy();
|
||||
|
||||
if (signature)
|
||||
dim_core_sig_destroy();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dim_core_exit(void)
|
||||
{
|
||||
dim_core_destroy_fs();
|
||||
dim_core_measure_destroy();
|
||||
dim_mem_pool_destroy();
|
||||
|
||||
if (signature)
|
||||
dim_core_sig_destroy();
|
||||
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
dim_check_memory_leak();
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(dim_core_init);
|
||||
module_exit(dim_core_exit);
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "dim_core_policy.h"
|
||||
#include "dim_core_mem_pool.h"
|
||||
#include "dim_core_static_baseline.h"
|
||||
#include "dim_core_measure_task.h"
|
||||
#include "dim_core_measure.h"
|
||||
|
||||
/* measurement tasks */
|
||||
static struct dim_measure_task *dim_core_tasks[] = {
|
||||
&dim_core_measure_task_user_text,
|
||||
&dim_core_measure_task_kernel_text,
|
||||
&dim_core_measure_task_module_text,
|
||||
};
|
||||
|
||||
/* the global measurement handle */
|
||||
struct dim_measure dim_core_handle = {
|
||||
.task_list = LIST_HEAD_INIT(dim_core_handle.task_list),
|
||||
};
|
||||
|
||||
/* lock to prevent trigger multiple measurement */
|
||||
DEFINE_MUTEX(dim_core_measure_lock);
|
||||
|
||||
/* dim measurement work */
|
||||
static struct workqueue_struct *dim_work_queue = NULL;
|
||||
static struct delayed_work dim_measure_work;
|
||||
static struct work_struct dim_baseline_work;
|
||||
|
||||
/* special measurement parameters for dim_core */
|
||||
static atomic_t measure_interval = ATOMIC_INIT(0);
|
||||
static atomic_t measure_action = ATOMIC_INIT(0);
|
||||
|
||||
/* interface to print measure status string */
|
||||
const char *dim_core_status_print(void)
|
||||
{
|
||||
return dim_measure_status_print(&dim_core_handle);
|
||||
}
|
||||
|
||||
/* interface to get tampered action */
|
||||
long dim_core_measure_action_get(void)
|
||||
{
|
||||
return atomic_read(&measure_action);
|
||||
}
|
||||
|
||||
/* interface to set measure action */
|
||||
int dim_core_measure_action_set(unsigned int act)
|
||||
{
|
||||
if (act >= DIM_MEASURE_ACTION_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
atomic_set(&measure_action, act);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* interface to get measure interval */
|
||||
long dim_core_interval_get(void)
|
||||
{
|
||||
return atomic_read(&measure_interval);
|
||||
}
|
||||
|
||||
/* interface to set measure interval */
|
||||
int dim_core_interval_set(unsigned int min)
|
||||
{
|
||||
unsigned long jiffies = 0;
|
||||
|
||||
if (min > DIM_INTERVAL_MAX ||
|
||||
(unsigned long)min * DIM_MINUTE_TO_SEC > MAX_SEC_IN_JIFFIES)
|
||||
return -ERANGE;
|
||||
|
||||
atomic_set(&measure_interval, min);
|
||||
if (min == 0) {
|
||||
dim_info("cancel dim timed measure work\n");
|
||||
cancel_delayed_work_sync(&dim_measure_work);
|
||||
} else {
|
||||
jiffies = nsecs_to_jiffies64((unsigned long)min *
|
||||
DIM_MINUTE_TO_NSEC);
|
||||
dim_info("modify dim measure interval to %u min "
|
||||
"(jittfies = 0x%lx)\n", min, jiffies);
|
||||
mod_delayed_work(dim_work_queue, &dim_measure_work, jiffies);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int baseline_prepare(struct dim_measure *m)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* 1. reload dim policy */
|
||||
ret = dim_core_policy_load();
|
||||
if (ret < 0) {
|
||||
dim_err("failed to load dim core policy: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 2. clear dim baseline */
|
||||
dim_baseline_destroy_tree(&m->static_baseline);
|
||||
dim_baseline_destroy_tree(&m->dynamic_baseline);
|
||||
|
||||
/* 3. reload dim baseline */
|
||||
ret = dim_core_static_baseline_load(m);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to load dim static baseline: %d\n", ret);
|
||||
dim_core_policy_destroy();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 4. refresh measure log */
|
||||
dim_measure_log_refresh(&m->log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void queue_delayed_measure_work(void)
|
||||
{
|
||||
unsigned long jiffies = 0;
|
||||
unsigned int interval = atomic_read(&measure_interval);
|
||||
|
||||
if (interval == 0)
|
||||
return;
|
||||
|
||||
jiffies = nsecs_to_jiffies64((unsigned long)interval *
|
||||
DIM_MINUTE_TO_NSEC);
|
||||
queue_delayed_work(dim_work_queue, &dim_measure_work, jiffies);
|
||||
}
|
||||
|
||||
static void measure_work_cb(struct work_struct *work)
|
||||
{
|
||||
dim_measure_task_measure(DIM_MEASURE, &dim_core_handle);
|
||||
queue_delayed_measure_work();
|
||||
}
|
||||
|
||||
static void baseline_work_cb(struct work_struct *work)
|
||||
{
|
||||
dim_measure_task_measure(DIM_BASELINE, &dim_core_handle);
|
||||
/* if baseline is failed, dont perform measurement */
|
||||
if (dim_measure_status_error(&dim_core_handle))
|
||||
return;
|
||||
|
||||
queue_delayed_measure_work();
|
||||
}
|
||||
|
||||
/* trigger a measurement and wait for it to complete */
|
||||
int dim_core_measure_blocking(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!mutex_trylock(&dim_core_measure_lock))
|
||||
return -EBUSY;
|
||||
|
||||
/* clean the running work */
|
||||
flush_delayed_work(&dim_measure_work);
|
||||
cancel_delayed_work_sync(&dim_measure_work);
|
||||
/* queue and flush measure work */
|
||||
queue_delayed_work(dim_work_queue, &dim_measure_work, 0);
|
||||
flush_delayed_work(&dim_measure_work);
|
||||
|
||||
/* check error status */
|
||||
if (dim_measure_status_error(&dim_core_handle))
|
||||
ret = -EFAULT;
|
||||
|
||||
mutex_unlock(&dim_core_measure_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* trigger a dynamic baseline and wait for it to complete */
|
||||
int dim_core_baseline_blocking(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!mutex_trylock(&dim_core_measure_lock))
|
||||
return -EBUSY;
|
||||
|
||||
/* clean the running work */
|
||||
flush_delayed_work(&dim_measure_work);
|
||||
cancel_delayed_work_sync(&dim_measure_work);
|
||||
|
||||
/* queue and flush baseline work */
|
||||
queue_work(dim_work_queue, &dim_baseline_work);
|
||||
flush_work(&dim_baseline_work);
|
||||
|
||||
/* check error status */
|
||||
if (dim_measure_status_error(&dim_core_handle))
|
||||
ret = -EFAULT;
|
||||
|
||||
mutex_unlock(&dim_core_measure_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dim_core_measure_init(struct dim_measure_cfg *cfg, unsigned int interval)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* set the special baseline memory functions */
|
||||
cfg->dyn_malloc = dim_mem_pool_alloc;
|
||||
cfg->dyn_free = dim_mem_pool_free;
|
||||
|
||||
/* init the measurement handle */
|
||||
ret = dim_measure_init(&dim_core_handle, cfg);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to init measurement handle\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set the baseline prepare function */
|
||||
dim_core_handle.baseline_prepare = baseline_prepare;
|
||||
|
||||
/* register all measurement tasks */
|
||||
ret = dim_measure_tasks_register(&dim_core_handle, dim_core_tasks,
|
||||
DIM_ARRAY_LEN(dim_core_tasks));
|
||||
if (ret < 0) {
|
||||
dim_err("failed to register measure tasks: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* init the measurement working thread */
|
||||
dim_work_queue = create_singlethread_workqueue("dim_core");
|
||||
if (dim_work_queue == NULL) {
|
||||
ret = -ENOMEM;
|
||||
dim_err("failed to create dim work queue: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* init the measurement work */
|
||||
INIT_WORK(&dim_baseline_work, baseline_work_cb);
|
||||
INIT_DELAYED_WORK(&dim_measure_work, measure_work_cb);
|
||||
|
||||
/* if the interval is set, start to do baseline and measure */
|
||||
if (interval) {
|
||||
ret = dim_core_baseline_blocking();
|
||||
if (ret < 0) {
|
||||
dim_err("failed to do baseline init: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = dim_core_interval_set(interval);
|
||||
if (ret < 0)
|
||||
dim_warn("failed to set measure interval: %d\n", ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dim_measure_destroy(&dim_core_handle);
|
||||
if (dim_work_queue != NULL) {
|
||||
destroy_workqueue(dim_work_queue);
|
||||
dim_work_queue = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dim_core_measure_destroy(void)
|
||||
{
|
||||
mutex_lock(&dim_core_measure_lock);
|
||||
if (dim_work_queue != NULL) {
|
||||
/* 1. wait the measure work to finish */
|
||||
flush_delayed_work(&dim_measure_work);
|
||||
cancel_delayed_work_sync(&dim_measure_work);
|
||||
/* 2. do clean job */
|
||||
destroy_workqueue(dim_work_queue);
|
||||
}
|
||||
|
||||
dim_measure_destroy(&dim_core_handle);
|
||||
dim_core_policy_destroy();
|
||||
mutex_unlock(&dim_core_measure_lock);
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_CORE_MEASURE_H
|
||||
#define __DIM_CORE_MEASURE_H
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
/* default configuration */
|
||||
#define DIM_CORE_HASH_DEFAULT "sha256"
|
||||
#define DIM_CORE_LOG_CAP_DEFAULT 100000
|
||||
|
||||
/* max measure interval = 1 year */
|
||||
#define DIM_INTERVAL_MAX (365 * 24 * 60)
|
||||
#define DIM_MINUTE_TO_SEC (60UL)
|
||||
#define DIM_MINUTE_TO_NSEC (60UL * 1000 * 1000 * 1000)
|
||||
|
||||
enum dim_measure_action {
|
||||
DIM_MEASURE_ACTION_DISABLE,
|
||||
DIM_MEASURE_ACTION_ENABLE,
|
||||
DIM_MEASURE_ACTION_MAX,
|
||||
};
|
||||
|
||||
extern struct dim_measure dim_core_handle;
|
||||
|
||||
/* global init and destroy */
|
||||
int dim_core_measure_init(struct dim_measure_cfg *cfg, unsigned int interval);
|
||||
void dim_core_measure_destroy(void);
|
||||
|
||||
/* control function for measurement parameters */
|
||||
const char *dim_core_status_print(void);
|
||||
long dim_core_measure_action_get(void);
|
||||
int dim_core_measure_action_set(unsigned int act);
|
||||
long dim_core_interval_get(void);
|
||||
int dim_core_interval_set(unsigned int p);
|
||||
long dim_core_tampered_action_get(void);
|
||||
int dim_core_tampered_action_set(unsigned int p);
|
||||
|
||||
/* measurement trigger functions */
|
||||
int dim_core_measure_blocking(void);
|
||||
int dim_core_baseline_blocking(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include "dim_safe_func.h"
|
||||
#include "dim_utils.h"
|
||||
|
||||
#include "dim_core_mem_pool.h"
|
||||
|
||||
static struct gen_pool *dim_pool = NULL;
|
||||
|
||||
static int dim_mem_pool_expand(unsigned int order)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
struct page *pages = NULL;
|
||||
unsigned long pages_addr = 0;
|
||||
size_t size = (1 << order) << PAGE_SHIFT;
|
||||
|
||||
pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
|
||||
if (pages == NULL) {
|
||||
dim_err("failed to allocate pages for memory pool\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pages_addr = (unsigned long)page_address(pages);
|
||||
dim_devel("alloc_pages: addr = 0x%lx, order = %d\n", pages_addr, order);
|
||||
|
||||
ret = gen_pool_add(dim_pool, pages_addr, size, -1);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to add pages to memory pool: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dim_devel("dim_mem_pool_expand: %lu\n", size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dim_mem_pool_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
dim_pool = gen_pool_create(DIM_MIN_ALLOC_ORDER, -1);
|
||||
if (dim_pool == NULL) {
|
||||
dim_err("failed to generate memory pool\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = dim_mem_pool_expand(DIM_EXPEND_ALLOC_PAGE_ORDER);
|
||||
if (ret < 0) {
|
||||
gen_pool_destroy(dim_pool);
|
||||
dim_pool = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void free_chunk(struct gen_pool *pool,
|
||||
struct gen_pool_chunk *chunk,
|
||||
void *data __always_unused)
|
||||
{
|
||||
if (chunk == NULL)
|
||||
return;
|
||||
|
||||
dim_devel("free_pages: addr = 0x%lx, order = %d\n",
|
||||
chunk->start_addr, DIM_EXPEND_ALLOC_PAGE_ORDER);
|
||||
free_pages(chunk->start_addr, DIM_EXPEND_ALLOC_PAGE_ORDER);
|
||||
}
|
||||
|
||||
void dim_mem_pool_destroy(void)
|
||||
{
|
||||
if (dim_pool == NULL)
|
||||
return;
|
||||
|
||||
if (gen_pool_avail(dim_pool) != gen_pool_size(dim_pool)) {
|
||||
dim_err("dim_mem_pool_destroy failed, memory leak detected\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gen_pool_for_each_chunk(dim_pool, free_chunk, NULL);
|
||||
gen_pool_destroy(dim_pool);
|
||||
dim_pool = NULL;
|
||||
}
|
||||
|
||||
void *dim_mem_pool_alloc(size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_pool_mem *data = NULL;
|
||||
size_t mem_size = size + sizeof(struct dim_pool_mem);
|
||||
|
||||
if (size > DIM_MAX_ALLOC_SIZE || mem_size > DIM_MAX_ALLOC_SIZE) {
|
||||
dim_err("memory pool over allocate size: %lu", size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = (struct dim_pool_mem *)gen_pool_alloc(dim_pool, mem_size);
|
||||
if (data != NULL)
|
||||
goto out;
|
||||
|
||||
dim_devel("gen_pool_alloc failed, try dim_mem_pool_expand\n");
|
||||
ret = dim_mem_pool_expand(DIM_EXPEND_ALLOC_PAGE_ORDER);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to expand memory pool: %d\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = (struct dim_pool_mem *)gen_pool_alloc(dim_pool, mem_size);
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
out:
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
dim_alloc_debug_inc();
|
||||
#endif
|
||||
data->size = mem_size;
|
||||
return data->data;
|
||||
}
|
||||
|
||||
void dim_mem_pool_free(const void *data)
|
||||
{
|
||||
struct dim_pool_mem *mem = NULL;
|
||||
|
||||
if (!gen_pool_has_addr(dim_pool, (uintptr_t)data, 1)) {
|
||||
dim_err("addr 0x%lx is not in the memory pool\n",
|
||||
(uintptr_t)data);
|
||||
return;
|
||||
}
|
||||
|
||||
mem = container_of(data, struct dim_pool_mem, data);
|
||||
if (!gen_pool_has_addr(dim_pool, (uintptr_t)mem, mem->size)) {
|
||||
dim_err("addr 0x%lx (size %lu) is not in the memory pool\n",
|
||||
(uintptr_t)mem, mem->size);
|
||||
return;
|
||||
}
|
||||
|
||||
gen_pool_free(dim_pool, (uintptr_t)mem, mem->size);
|
||||
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
dim_alloc_debug_dec();
|
||||
#endif
|
||||
}
|
||||
|
||||
void dim_mem_pool_walk_chunk(pool_chunk_visitor f, void *data)
|
||||
{
|
||||
gen_pool_for_each_chunk(dim_pool, f, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dim_mem_pool_walk_chunk);
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_CORE_MEM_POOL
|
||||
#define __DIM_CORE_MEM_POOL
|
||||
|
||||
#include <linux/genalloc.h>
|
||||
|
||||
/* Mininum allocation: 2 ^ 3 = 8 bytes */
|
||||
#define DIM_MIN_ALLOC_ORDER 3
|
||||
/* Expand allocation: 2 ^ 2 * 4k = 16k */
|
||||
#define DIM_EXPEND_ALLOC_PAGE_ORDER 2
|
||||
#define DIM_MAX_ALLOC_SIZE ((1 << DIM_EXPEND_ALLOC_PAGE_ORDER) << PAGE_SHIFT)
|
||||
|
||||
struct dim_pool_mem {
|
||||
size_t size;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
typedef void (*pool_chunk_visitor)(struct gen_pool *,
|
||||
struct gen_pool_chunk *,
|
||||
void *);
|
||||
|
||||
int dim_mem_pool_init(void);
|
||||
void dim_mem_pool_destroy(void);
|
||||
void *dim_mem_pool_alloc(size_t size);
|
||||
void dim_mem_pool_free(const void *data);
|
||||
void dim_mem_pool_walk_chunk(pool_chunk_visitor f, void *data);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <crypto/public_key.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/kernel_read_file.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
|
||||
#include "dim_hash.h"
|
||||
#include "dim_utils.h"
|
||||
#include "dim_safe_func.h"
|
||||
|
||||
#include "dim_core_sig.h"
|
||||
|
||||
static struct key *dim_core_keyring = NULL;
|
||||
static struct key *dim_core_key = NULL;
|
||||
static struct dim_hash dim_core_sig_hash = { 0 };
|
||||
|
||||
static char *add_suffix(const char *str, const char *suffix)
|
||||
{
|
||||
int len = 0;
|
||||
char *buf = NULL;
|
||||
|
||||
len = strlen(str) + strlen(suffix) + 1;
|
||||
buf = dim_kzalloc_gfp(len);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
sprintf(buf, "%s%s", str, suffix);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int read_file_root(struct path *root, const char *name, void **buf)
|
||||
{
|
||||
int ret = 0;
|
||||
struct file *file = NULL;
|
||||
|
||||
if (root == NULL) {
|
||||
ret = kernel_read_file_from_path(name, 0, buf,
|
||||
DIM_CORE_MAX_FILE_SIZE,
|
||||
NULL, READING_UNKNOWN);
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
if (*buf != NULL)
|
||||
dim_alloc_debug_inc();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
file = file_open_root(root, name, O_RDONLY, 0);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
ret = kernel_read_file(file, 0, buf, DIM_CORE_MAX_FILE_SIZE,
|
||||
NULL, READING_UNKNOWN);
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
if (*buf != NULL)
|
||||
dim_alloc_debug_inc();
|
||||
#endif
|
||||
(void)filp_close(file, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dim_core_sig_verify(const char *buf, loff_t buf_len,
|
||||
const char *sbuf, loff_t sbuf_len)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_digest digest = { 0 };
|
||||
/* Currently only support RSA-SHA256 */
|
||||
struct public_key_signature key_sig = {
|
||||
.pkey_algo = "rsa",
|
||||
.hash_algo = "sha256",
|
||||
.encoding = "pkcs1"
|
||||
};
|
||||
|
||||
if (buf == NULL || sbuf == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ret = dim_hash_calculate(buf, buf_len, &dim_core_sig_hash, &digest);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
key_sig.s = (char *)sbuf;
|
||||
key_sig.s_size = sbuf_len;
|
||||
key_sig.digest = digest.data;
|
||||
key_sig.digest_size = dim_digest_size(digest.algo);
|
||||
|
||||
return verify_signature(dim_core_key, &key_sig);
|
||||
}
|
||||
|
||||
int dim_read_verify_file(struct path *root, const char *name, void **buf)
|
||||
{
|
||||
int ret = 0;
|
||||
char *sig_name = NULL;
|
||||
void *file_buf = NULL;
|
||||
void *sig_buf = NULL;
|
||||
size_t file_size = 0;
|
||||
size_t sig_size = 0;
|
||||
|
||||
if (name == NULL || buf == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
sig_name = add_suffix(name, DIM_CORE_SIG_FILE_SUFFIX);
|
||||
if (sig_name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = read_file_root(root, name, &file_buf);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
file_size = ret;
|
||||
ret = 0;
|
||||
|
||||
if (dim_core_key == NULL)
|
||||
goto out; /* no need to verify signature */
|
||||
|
||||
ret = read_file_root(root, sig_name, &sig_buf);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
sig_size = ret;
|
||||
ret = dim_core_sig_verify(file_buf, file_size, sig_buf, sig_size);
|
||||
out:
|
||||
dim_kfree(sig_name);
|
||||
dim_vfree(sig_buf);
|
||||
if (ret < 0)
|
||||
dim_vfree(file_buf);
|
||||
if (ret == 0) {
|
||||
*buf = file_buf;
|
||||
ret = file_size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dim_core_sig_init(void)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
void *data = NULL;
|
||||
key_ref_t key;
|
||||
|
||||
dim_core_keyring = keyring_alloc(DIM_CORE_KEYRING_NAME, KUIDT_INIT(0),
|
||||
KGIDT_INIT(0), current_cred(),
|
||||
DIM_CORE_KEYRING_PERM,
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
|
||||
if (IS_ERR(dim_core_keyring)) {
|
||||
ret = PTR_ERR(dim_core_keyring);
|
||||
dim_err("failed to allocate DIM keyring: %ld\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = kernel_read_file_from_path(DIM_CORE_CERT_PATH, 0, &data,
|
||||
DIM_CORE_MAX_FILE_SIZE, NULL,
|
||||
READING_X509_CERTIFICATE);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to read DIM cert file: %ld\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
key = key_create_or_update(make_key_ref(dim_core_keyring, 1),
|
||||
"asymmetric", NULL, data, ret,
|
||||
DIM_CORE_KEY_PERM, KEY_ALLOC_NOT_IN_QUOTA);
|
||||
if (IS_ERR(key)) {
|
||||
ret = PTR_ERR(key);
|
||||
dim_err("failed to load DIM cert: %ld\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = dim_hash_init("sha256", &dim_core_sig_hash);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to init dim signature hash: %ld\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
key_ref_put(key);
|
||||
dim_core_key = key_ref_to_ptr(key);
|
||||
dim_info("load DIM cert: %s\n", dim_core_key->description);
|
||||
ret = 0;
|
||||
err:
|
||||
dim_vfree(data);
|
||||
if (ret < 0) {
|
||||
key_put(dim_core_keyring);
|
||||
dim_core_keyring = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dim_core_sig_destroy(void)
|
||||
{
|
||||
if (dim_core_keyring != NULL)
|
||||
key_put(dim_core_keyring);
|
||||
|
||||
dim_hash_destroy(&dim_core_sig_hash);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_CORE_SIG_H
|
||||
#define __DIM_CORE_SIG_H
|
||||
|
||||
#include <linux/key.h>
|
||||
|
||||
#define DIM_CORE_MAX_FILE_SIZE (10 * 1024 * 1024)
|
||||
#define DIM_CORE_KEYRING_NAME "_dim"
|
||||
#define DIM_CORE_CERT_PATH "/etc/keys/x509_dim.der"
|
||||
#define DIM_CORE_SIG_FILE_SUFFIX ".sig"
|
||||
#define DIM_CORE_KEYRING_PERM ((KEY_POS_ALL & ~KEY_POS_SETATTR) \
|
||||
| KEY_USR_VIEW | KEY_USR_READ \
|
||||
| KEY_USR_WRITE | KEY_USR_SEARCH)
|
||||
#define DIM_CORE_KEY_PERM ((KEY_POS_ALL & ~KEY_POS_SETATTR) \
|
||||
| KEY_USR_VIEW | KEY_USR_READ)
|
||||
|
||||
int dim_core_sig_init(void);
|
||||
void dim_core_sig_destroy(void);
|
||||
int dim_read_verify_file(struct path *root, const char *name, void **buf);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
#include "dim_symbol.h"
|
||||
#include "dim_utils.h"
|
||||
|
||||
#include "dim_core_symbol.h"
|
||||
|
||||
struct dim_core_kallsyms dim_core_kernel_symbol;
|
||||
|
||||
int dim_core_kallsyms_init(void)
|
||||
{
|
||||
struct dim_core_kallsyms *k = &dim_core_kernel_symbol;
|
||||
DIM_SYMBOL_LOOKUP_FUNC dim_kallsyms_lookup_name = NULL;
|
||||
|
||||
if (memset(k, 0,
|
||||
sizeof(struct dim_core_kallsyms)) != k)
|
||||
return -EINVAL;
|
||||
|
||||
dim_kallsyms_lookup_name = dim_get_symbol_lookup_func();
|
||||
if (dim_kallsyms_lookup_name == NULL) {
|
||||
dim_err("failed to get symbol_lookup_func\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
k->stext = (char *)dim_kallsyms_lookup_name("_stext");
|
||||
k->etext = (char *)dim_kallsyms_lookup_name("_etext");
|
||||
|
||||
k->start_jump_table = (struct jump_entry *)
|
||||
dim_kallsyms_lookup_name("__start___jump_table");
|
||||
k->stop_jump_table = (struct jump_entry *)
|
||||
dim_kallsyms_lookup_name("__stop___jump_table");
|
||||
k->jump_label_lock = (DIM_JUMP_LABEL_LOCK)
|
||||
dim_kallsyms_lookup_name("jump_label_lock");
|
||||
k->jump_label_unlock = (DIM_JUMP_LABEL_UNLOCK)
|
||||
dim_kallsyms_lookup_name("jump_label_unlock");
|
||||
k->walk_process_tree = (DIM_WALK_PROCESS_TREE)
|
||||
dim_kallsyms_lookup_name("walk_process_tree");
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||
k->find_module = (DIM_FIND_MODULE)
|
||||
dim_kallsyms_lookup_name("find_module");
|
||||
k->find_get_task_by_vpid = (DIM_FIND_GET_TASK_BY_VPID)
|
||||
dim_kallsyms_lookup_name("find_get_task_by_vpid");
|
||||
#endif
|
||||
|
||||
return (k->stext == NULL || k->etext == NULL ||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||
k->find_module == NULL || k->find_get_task_by_vpid == NULL ||
|
||||
#endif
|
||||
k->start_jump_table == NULL || k->stop_jump_table == NULL ||
|
||||
k->jump_label_lock == NULL || k->jump_label_lock == NULL ||
|
||||
k->walk_process_tree == NULL) ? -ENOENT : 0;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_CORE_SYMBOL_H
|
||||
#define __DIM_CORE_SYMBOL_H
|
||||
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
typedef void (*DIM_JUMP_LABEL_LOCK)(void);
|
||||
typedef void (*DIM_JUMP_LABEL_UNLOCK)(void);
|
||||
typedef void (*DIM_WALK_PROCESS_TREE)(struct task_struct *,
|
||||
proc_visitor, void *);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||
typedef struct module *(*DIM_FIND_MODULE)(const char *);
|
||||
typedef struct task_struct *(*DIM_FIND_GET_TASK_BY_VPID)(pid_t);
|
||||
#endif
|
||||
#ifndef JUMP_LABEL_NOP_SIZE
|
||||
typedef int (*DIM_ARCH_JUMP_ENTRY_SIZE)(struct jump_entry *);
|
||||
#endif
|
||||
|
||||
|
||||
struct dim_core_kallsyms {
|
||||
char *stext;
|
||||
char *etext;
|
||||
struct jump_entry *start_jump_table;
|
||||
struct jump_entry *stop_jump_table;
|
||||
DIM_JUMP_LABEL_LOCK jump_label_lock;
|
||||
DIM_JUMP_LABEL_LOCK jump_label_unlock;
|
||||
DIM_WALK_PROCESS_TREE walk_process_tree;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||
DIM_FIND_MODULE find_module;
|
||||
DIM_FIND_GET_TASK_BY_VPID find_get_task_by_vpid;
|
||||
#endif
|
||||
#ifndef JUMP_LABEL_NOP_SIZE
|
||||
DIM_ARCH_JUMP_ENTRY_SIZE arch_jump_entry_size;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern struct dim_core_kallsyms dim_core_kernel_symbol;
|
||||
|
||||
int dim_core_kallsyms_init(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#include "dim_rb.h"
|
||||
#include "dim_utils.h"
|
||||
#include "dim_safe_func.h"
|
||||
|
||||
#include "dim_core_sig.h"
|
||||
#include "dim_core_policy.h"
|
||||
|
||||
static struct rb_root policy_root = RB_ROOT;
|
||||
|
||||
static int dim_policy_compare(struct dim_policy *x, struct dim_policy *y)
|
||||
{
|
||||
if (x->obj != y->obj)
|
||||
return x->obj - y->obj;
|
||||
|
||||
switch (x->obj) {
|
||||
case DIM_POLICY_OBJ_BPRM_TEXT:
|
||||
return dim_strcmp(x->path, y->path);
|
||||
case DIM_POLICY_OBJ_MODULE_TEXT:
|
||||
return dim_strcmp(x->name, y->name);
|
||||
case DIM_POLICY_OBJ_KERNEL_TEXT:
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
static int dim_policy_rb_find(struct rb_root *root,
|
||||
struct dim_policy *data,
|
||||
struct dim_policy **find_data)
|
||||
*/
|
||||
dim_rb_find(dim_policy);
|
||||
/*
|
||||
static int dim_policy_rb_add(struct rb_root *root,
|
||||
struct dim_policy *data,
|
||||
struct dim_policy **find_data)
|
||||
*/
|
||||
dim_rb_add(dim_policy);
|
||||
|
||||
void policy_destroy(struct dim_policy *policy)
|
||||
{
|
||||
if (policy == NULL)
|
||||
return;
|
||||
|
||||
dim_kfree(policy->name);
|
||||
dim_kfree(policy->path);
|
||||
dim_kfree(policy);
|
||||
}
|
||||
|
||||
static int policy_check_add_bprm_text(struct dim_policy *policy)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *apath = NULL;
|
||||
struct dim_policy *p = NULL;
|
||||
|
||||
/* check the policy is valid */
|
||||
if (policy->path == NULL) {
|
||||
dim_err("path must be set for BPRM_TEXT policy\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strlen(policy->path) + 1 > PATH_MAX) {
|
||||
dim_err("path must be shorter than %d\n", PATH_MAX);
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
|
||||
if (policy->name != NULL)
|
||||
dim_warn("name is ignored for BPRM_TEXT policy\n");
|
||||
|
||||
/* firstly, add the current node */
|
||||
ret = dim_policy_rb_add(&policy_root, policy, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* secondly, try to add another node with absolute path. beacuse
|
||||
sometimes users may not sure whether to write /usr/bin/bash
|
||||
or /bin/bash in policy */
|
||||
ret = dim_get_absolute_path(policy->path, &apath);
|
||||
if (ret < 0) {
|
||||
dim_warn("failed to get absolute path of %s in policy: %d\n",
|
||||
policy->path, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dim_strcmp(apath, policy->path) == 0) {
|
||||
/* the two paths are same, no need to add another policy */
|
||||
dim_kfree(apath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = dim_kmemdup_gfp(policy, sizeof(struct dim_policy));
|
||||
if (p == NULL) {
|
||||
dim_kfree(apath);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* set the absolute path and add the policy node */
|
||||
p->path = apath;
|
||||
ret = dim_policy_rb_add(&policy_root, p, NULL);
|
||||
if (ret < 0)
|
||||
policy_destroy(p);
|
||||
|
||||
/* the EEXIST error must be processed here */
|
||||
return ret == -EEXIST ? 0 : ret;
|
||||
}
|
||||
|
||||
static int policy_check_add_module_text(struct dim_policy *policy)
|
||||
{
|
||||
if (policy->name == NULL) {
|
||||
dim_err("name must be set for MODULE_TEXT policy\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strlen(policy->name) + 1 > NAME_MAX) {
|
||||
dim_err("name must be shorter than %d\n", NAME_MAX);
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
|
||||
if (policy->path != NULL)
|
||||
dim_warn("path is ignored for MODULE_TEXT policy\n");
|
||||
|
||||
if (policy->action != DIM_POLICY_ACTION_LOG)
|
||||
dim_warn("action is ignored for MODULE_TEXT policy\n");
|
||||
|
||||
return dim_policy_rb_add(&policy_root, policy, NULL);
|
||||
}
|
||||
|
||||
static int policy_check_add_kernel_text(struct dim_policy *policy)
|
||||
{
|
||||
if (policy->name != NULL || policy->path != NULL ||
|
||||
policy->action != DIM_POLICY_ACTION_LOG)
|
||||
dim_warn("all parameters are ignored for KERNEL_TEXT policy\n");
|
||||
|
||||
return dim_policy_rb_add(&policy_root, policy, NULL);
|
||||
}
|
||||
|
||||
static int policy_check_add(struct dim_policy *policy)
|
||||
{
|
||||
switch (policy->obj)
|
||||
{
|
||||
case DIM_POLICY_OBJ_BPRM_TEXT:
|
||||
return policy_check_add_bprm_text(policy);
|
||||
case DIM_POLICY_OBJ_MODULE_TEXT:
|
||||
return policy_check_add_module_text(policy);
|
||||
case DIM_POLICY_OBJ_KERNEL_TEXT:
|
||||
return policy_check_add_kernel_text(policy);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int dim_core_policy_load(void)
|
||||
{
|
||||
int ret = 0;
|
||||
void *buf = NULL;
|
||||
loff_t buf_len = 0;
|
||||
|
||||
if (!RB_EMPTY_ROOT(&policy_root))
|
||||
dim_core_policy_destroy();
|
||||
|
||||
ret = dim_read_verify_file(NULL, DIM_POLICY_PATH, &buf);
|
||||
if (ret < 0 || buf == NULL) {
|
||||
dim_err("failed to read policy file: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf_len = ret;
|
||||
ret = dim_policy_parse(buf, buf_len, policy_check_add);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to parse policy: %d\n", ret);
|
||||
dim_core_policy_destroy();
|
||||
}
|
||||
|
||||
dim_vfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dim_core_policy_destroy(void)
|
||||
{
|
||||
struct dim_policy *pos = NULL;
|
||||
struct dim_policy *n = NULL;
|
||||
|
||||
rbtree_postorder_for_each_entry_safe(pos, n, &policy_root, rb_node)
|
||||
policy_destroy(pos);
|
||||
|
||||
policy_root = RB_ROOT;
|
||||
}
|
||||
|
||||
static int policy_find(int obj, int key __always_unused, const char *val,
|
||||
struct dim_policy **find)
|
||||
{
|
||||
struct dim_policy policy = { 0 };
|
||||
|
||||
/* now the key parameter is unused */
|
||||
switch (obj) {
|
||||
case DIM_POLICY_OBJ_BPRM_TEXT:
|
||||
policy.path = val;
|
||||
break;
|
||||
case DIM_POLICY_OBJ_MODULE_TEXT:
|
||||
policy.name = val;
|
||||
break;
|
||||
case DIM_POLICY_OBJ_KERNEL_TEXT:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
policy.obj = obj;
|
||||
return dim_policy_rb_find(&policy_root, &policy, find);
|
||||
}
|
||||
|
||||
bool dim_core_policy_match(int obj, int key, const char *val)
|
||||
{
|
||||
if (val == NULL)
|
||||
return false;
|
||||
|
||||
return policy_find(obj, key, val, NULL) == 0;
|
||||
}
|
||||
|
||||
int dim_core_policy_get_action(int obj, int key, const char *val)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_policy *find = NULL;
|
||||
|
||||
if (val == NULL)
|
||||
return DIM_POLICY_ACTION_LAST;
|
||||
|
||||
ret = policy_find(obj, key, val, &find);
|
||||
if (ret < 0)
|
||||
return DIM_POLICY_ACTION_LAST;
|
||||
|
||||
return find->action;
|
||||
}
|
||||
|
||||
int dim_core_policy_walk(int (*f)(struct dim_policy *, void *), void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_policy *pos = NULL;
|
||||
struct dim_policy *n = NULL;
|
||||
|
||||
rbtree_postorder_for_each_entry_safe(pos, n, &policy_root, rb_node) {
|
||||
ret = f(pos, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_CORE_POLICY_H
|
||||
#define __DIM_CORE_POLICY_H
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
/* the policy filepath */
|
||||
#define DIM_POLICY_PATH "/etc/dim/policy"
|
||||
/* max number of lines for parsing */
|
||||
#define DIM_POLICY_LINE_MAX 100000
|
||||
|
||||
/* measurement object of policy */
|
||||
enum dim_policy_obj {
|
||||
DIM_POLICY_OBJ_BPRM_TEXT,
|
||||
DIM_POLICY_OBJ_MODULE_TEXT,
|
||||
DIM_POLICY_OBJ_KERNEL_TEXT,
|
||||
DIM_POLICY_OBJ_LAST,
|
||||
};
|
||||
|
||||
/* key of measurement condition */
|
||||
enum dim_policy_key {
|
||||
DIM_POLICY_KEY_OBJ,
|
||||
DIM_POLICY_KEY_NAME,
|
||||
DIM_POLICY_KEY_PATH,
|
||||
DIM_POLICY_KEY_ACTION,
|
||||
DIM_POLICY_KEY_LAST,
|
||||
};
|
||||
|
||||
/* action to perform when dim detected a tampering */
|
||||
enum dim_policy_action {
|
||||
/* add to measure log (default) */
|
||||
DIM_POLICY_ACTION_LOG,
|
||||
/* kill the tampered user process */
|
||||
DIM_POLICY_ACTION_KILL,
|
||||
DIM_POLICY_ACTION_LAST,
|
||||
};
|
||||
|
||||
struct dim_policy {
|
||||
struct rb_node rb_node;
|
||||
int obj; /* enum dim_policy_obj */
|
||||
const char *path; /* user process path */
|
||||
const char *name; /* module name */
|
||||
int action; /* enum dim_policy_action */
|
||||
};
|
||||
|
||||
/* callback funtion to walk dim policy nodes */
|
||||
typedef int (*dim_policy_visitor)(struct dim_policy *, void *);
|
||||
|
||||
/* callback funtion to add a policy item when parsing policy file */
|
||||
typedef int (*policy_add_func)(struct dim_policy *);
|
||||
|
||||
/* parse dim policy in complex format */
|
||||
int policy_parse_complex_format(char *buf, size_t buf_len,
|
||||
policy_add_func policy_add);
|
||||
#define dim_policy_parse policy_parse_complex_format
|
||||
|
||||
/* export for implementing the policy parser */
|
||||
void policy_destroy(struct dim_policy *policy);
|
||||
|
||||
int dim_core_policy_load(void);
|
||||
void dim_core_policy_destroy(void);
|
||||
bool dim_core_policy_match(int obj, int key, const char *val);
|
||||
int dim_core_policy_walk(dim_policy_visitor f, void *data);
|
||||
int dim_core_policy_get_action(int obj, int key, const char *val);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dim_rb.h"
|
||||
#include "dim_utils.h"
|
||||
#include "dim_safe_func.h"
|
||||
|
||||
#include "dim_core_policy.h"
|
||||
|
||||
/* policy key */
|
||||
#define DIM_POLICY_MEASURE "measure"
|
||||
|
||||
/* measure obj=MODULE_TEXT path={PATH_MAX} action=kill\n */
|
||||
#define DIM_POLICY_MAX_KEY_FIELDS 3
|
||||
#define DIM_POLICY_OBJ_MAX_LEN 15
|
||||
#define DIM_POLICY_KEY_MAX_LEN 5
|
||||
#define DIM_POLICY_ACTION_MAX_LEN 11
|
||||
#define DIM_POLICY_MAX_LEN (strlen(DIM_POLICY_MEASURE) + 1 + \
|
||||
DIM_POLICY_OBJ_MAX_LEN + 1 + \
|
||||
DIM_POLICY_KEY_MAX_LEN + 1 + PATH_MAX + 1 + \
|
||||
DIM_POLICY_ACTION_MAX_LEN + 1)
|
||||
|
||||
static const char *dim_policy_key_str[DIM_POLICY_KEY_LAST] = {
|
||||
[DIM_POLICY_KEY_OBJ] = "obj=",
|
||||
[DIM_POLICY_KEY_NAME] = "name=",
|
||||
[DIM_POLICY_KEY_PATH] = "path=",
|
||||
[DIM_POLICY_KEY_ACTION] = "action=",
|
||||
};
|
||||
|
||||
static const char *dim_policy_obj_str[DIM_POLICY_OBJ_LAST] = {
|
||||
[DIM_POLICY_OBJ_BPRM_TEXT] = "BPRM_TEXT",
|
||||
[DIM_POLICY_OBJ_MODULE_TEXT] = "MODULE_TEXT",
|
||||
[DIM_POLICY_OBJ_KERNEL_TEXT] = "KERNEL_TEXT",
|
||||
};
|
||||
|
||||
static const char *dim_policy_action_str[DIM_POLICY_KEY_LAST] = {
|
||||
[DIM_POLICY_ACTION_LOG] = "log",
|
||||
[DIM_POLICY_ACTION_KILL] = "kill",
|
||||
};
|
||||
|
||||
static const char *policy_get_string_value(const char *s)
|
||||
{
|
||||
return dim_kstrdup_gfp(s);
|
||||
}
|
||||
|
||||
static int policy_get_action(const char *s)
|
||||
{
|
||||
return match_string(dim_policy_action_str, DIM_POLICY_ACTION_LAST, s);
|
||||
}
|
||||
|
||||
static int policy_get_obj(const char *s)
|
||||
{
|
||||
return match_string(dim_policy_obj_str, DIM_POLICY_OBJ_LAST, s);
|
||||
}
|
||||
|
||||
static int policy_get_key(const char *s, const char **val)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
unsigned int len = 0;
|
||||
|
||||
for (; i < DIM_POLICY_KEY_LAST; i++) {
|
||||
len = strlen(dim_policy_key_str[i]);
|
||||
if (dim_strncmp(s, dim_policy_key_str[i], len) == 0) {
|
||||
*val = s + len;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int policy_parse_key_value(char *s, struct dim_policy *policy)
|
||||
{
|
||||
char *p = NULL;
|
||||
int key = 0;
|
||||
int filed_num = 0;
|
||||
const char *val = NULL;
|
||||
|
||||
while ((p = strsep(&s, " ")) != NULL) {
|
||||
key = policy_get_key(p, &val);
|
||||
if (key < 0 || val == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (++filed_num > DIM_POLICY_MAX_KEY_FIELDS)
|
||||
return -EINVAL;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case DIM_POLICY_KEY_OBJ:
|
||||
policy->obj = policy_get_obj(val);
|
||||
if (policy->obj < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case DIM_POLICY_KEY_NAME:
|
||||
policy->name = policy_get_string_value(val);
|
||||
if (policy->name == NULL)
|
||||
return -ENOMEM;
|
||||
break;
|
||||
case DIM_POLICY_KEY_PATH:
|
||||
policy->path = policy_get_string_value(val);
|
||||
if (policy->path == NULL)
|
||||
return -ENOMEM;
|
||||
break;
|
||||
case DIM_POLICY_KEY_ACTION:
|
||||
policy->action = policy_get_action(val);
|
||||
if (policy->action < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_line(char *line_str, struct dim_policy *policy)
|
||||
{
|
||||
int ret = 0;
|
||||
char *p = NULL;
|
||||
|
||||
if ((p = strsep(&line_str, " ")) == NULL ||
|
||||
dim_strcmp(p, DIM_POLICY_MEASURE) != 0) {
|
||||
dim_err("invalid policy prefix, must start with %s\n",
|
||||
DIM_POLICY_MEASURE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = policy_parse_key_value(line_str, policy);
|
||||
if (ret < 0) {
|
||||
dim_err("fail to parse policy key and value: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int policy_parse_line(char* line, int line_no, void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_policy *policy = NULL;
|
||||
policy_add_func policy_add = data;
|
||||
|
||||
if (line_no > DIM_POLICY_LINE_MAX) {
|
||||
dim_warn("more than %d policy items will be ignored\n",
|
||||
DIM_POLICY_LINE_MAX);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (strlen(line) == 0 || line[0] == '#')
|
||||
return 0; /* ignore blank line and comment */
|
||||
|
||||
if (strlen(line) > DIM_POLICY_MAX_LEN) {
|
||||
dim_err("overlength line %d\n", line_no);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
policy = dim_kzalloc_gfp(sizeof(struct dim_policy));
|
||||
if (policy == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = parse_line(line, policy);
|
||||
if (ret < 0) {
|
||||
policy_destroy(policy);
|
||||
dim_err("failed to parse policy at line %d: %d\n", line_no, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = policy_add(policy);
|
||||
if (ret < 0) {
|
||||
policy_destroy(policy);
|
||||
/* ignore the repeat add */
|
||||
if (ret != -EEXIST)
|
||||
dim_err("failed to add policy at line %d: %d\n", line_no, ret);
|
||||
return ret == -EEXIST ? 0 : ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int policy_parse_complex_format(char *buf, size_t buf_len,
|
||||
policy_add_func policy_add)
|
||||
{
|
||||
if (buf == NULL || buf_len == 0 || policy_add == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return dim_parse_line_buf(buf, buf_len, policy_parse_line, policy_add);
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "dim_utils.h"
|
||||
#include "dim_hash.h"
|
||||
|
||||
#include "dim_core_sig.h"
|
||||
#include "dim_core_policy.h"
|
||||
#include "dim_core_measure.h"
|
||||
#include "dim_core_static_baseline.h"
|
||||
|
||||
#define BASELINE_FILE_SUFFIX ".hash"
|
||||
#define BASELINE_FILE_SUFFIX_LEN 5
|
||||
|
||||
static bool baseline_match_policy(const char *name, int type)
|
||||
{
|
||||
const char *kr = init_uts_ns.name.release;
|
||||
unsigned int kr_len = strlen(kr);
|
||||
unsigned int name_len = strlen(name);
|
||||
const char *mod_name = NULL;
|
||||
|
||||
if (type != DIM_BASELINE_KERNEL)
|
||||
return dim_core_policy_match(DIM_POLICY_OBJ_BPRM_TEXT,
|
||||
DIM_POLICY_KEY_PATH, name);
|
||||
|
||||
if (dim_strcmp(name, kr) == 0)
|
||||
return dim_core_policy_match(DIM_POLICY_OBJ_KERNEL_TEXT,
|
||||
DIM_POLICY_KEY_NAME, kr);
|
||||
|
||||
if (name_len <= kr_len + 2 || /* <kernel release>/<mod_name> */
|
||||
dim_strncmp(kr, name, kr_len) != 0 ||
|
||||
*(name + kr_len) != '/')
|
||||
return false;
|
||||
|
||||
mod_name = name + kr_len + 1;
|
||||
return dim_core_policy_match(DIM_POLICY_OBJ_MODULE_TEXT,
|
||||
DIM_POLICY_KEY_NAME, mod_name);
|
||||
}
|
||||
|
||||
static int baseline_check_add(const char *name, int type,
|
||||
struct dim_digest *digest,
|
||||
struct dim_measure *m)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *real_path = NULL;
|
||||
|
||||
if (type == DIM_BASELINE_KERNEL)
|
||||
return dim_measure_static_baseline_add(m, name, type, digest);
|
||||
|
||||
/* for process, try to add the absolute path */
|
||||
ret = dim_get_absolute_path(name, &real_path);
|
||||
if (ret < 0) {
|
||||
dim_warn("failed to get absolute path of %s in static baeline: %d\n",
|
||||
name, ret);
|
||||
return dim_measure_static_baseline_add(m, name, type, digest);
|
||||
}
|
||||
|
||||
ret = dim_measure_static_baseline_add(m, real_path, type, digest);
|
||||
dim_kfree(real_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct name_entry {
|
||||
char name[NAME_MAX];
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
static int
|
||||
#else
|
||||
static bool
|
||||
#endif
|
||||
baseline_fill_dir(struct dir_context *__ctx,
|
||||
const char *name,
|
||||
int name_len,
|
||||
loff_t offset,
|
||||
unsigned long long ino,
|
||||
unsigned d_type)
|
||||
{
|
||||
struct baseline_parse_ctx *ctx = container_of(__ctx, typeof(*ctx), ctx);
|
||||
struct name_entry *entry = NULL;
|
||||
|
||||
/* baseline file must end with '.hash' */
|
||||
if (d_type != DT_REG || name_len >= NAME_MAX ||
|
||||
name_len <= BASELINE_FILE_SUFFIX_LEN ||
|
||||
strncmp(name + name_len - BASELINE_FILE_SUFFIX_LEN,
|
||||
BASELINE_FILE_SUFFIX, BASELINE_FILE_SUFFIX_LEN))
|
||||
goto out; /* ignore invalid files */
|
||||
|
||||
entry = dim_kzalloc_gfp(sizeof(struct name_entry));
|
||||
if (entry == NULL)
|
||||
goto out;
|
||||
|
||||
strncpy(entry->name, name, name_len);
|
||||
list_add( &entry->list, &ctx->name_list);
|
||||
out:
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
return 0; /* ignore fail */
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
int dim_core_static_baseline_load(struct dim_measure *m)
|
||||
{
|
||||
int ret = 0;
|
||||
struct path kpath;
|
||||
struct file *file = NULL;
|
||||
struct name_entry *entry = NULL;
|
||||
struct name_entry *tmp = NULL;
|
||||
void *buf = NULL;
|
||||
unsigned long buf_len = 0;
|
||||
struct baseline_parse_ctx ctx = {
|
||||
.m = m,
|
||||
.ctx.actor = baseline_fill_dir,
|
||||
.add = baseline_check_add,
|
||||
.match = baseline_match_policy,
|
||||
.name_list = LIST_HEAD_INIT(ctx.name_list)
|
||||
};
|
||||
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kern_path(DIM_STATIC_BASELINE_ROOT, LOOKUP_DIRECTORY, &kpath);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to get dim baseline root path: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
file = filp_open(DIM_STATIC_BASELINE_ROOT, O_RDONLY | O_DIRECTORY, 0);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
dim_err("failed to open %s: %d\n", DIM_STATIC_BASELINE_ROOT, ret);
|
||||
path_put(&kpath);
|
||||
return ret;
|
||||
}
|
||||
|
||||
(void)iterate_dir(file, &ctx.ctx);
|
||||
filp_close(file, NULL);
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &ctx.name_list, list) {
|
||||
ret = dim_read_verify_file(&kpath, entry->name, &buf);
|
||||
if (ret < 0 || buf == NULL) {
|
||||
dim_err("failed to read and verify %s: %d\n", entry->name, ret);
|
||||
dim_kfree(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
buf_len = ret;
|
||||
ret = dim_baseline_parse(buf, buf_len, &ctx);
|
||||
if (ret < 0)
|
||||
dim_err("failed to parse baseline file %s: %d\n", entry->name, ret);
|
||||
|
||||
dim_vfree(buf);
|
||||
dim_kfree(entry);
|
||||
}
|
||||
|
||||
path_put(&kpath);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_CORE_STATIC_BASELINE_H
|
||||
#define __DIM_CORE_STATIC_BASELINE_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
/* directory to store the static baseline files */
|
||||
#define DIM_STATIC_BASELINE_ROOT "/etc/dim/digest_list"
|
||||
|
||||
/* callback function to check if a baseline is matched the policy */
|
||||
typedef bool (*baseline_match_func)(const char *name, int type);
|
||||
|
||||
/* callback function to add baseline to measurement handle */
|
||||
typedef int (*baseline_add_func)(const char *name, int type,
|
||||
struct dim_digest *digest,
|
||||
struct dim_measure *m);
|
||||
|
||||
/* the context used in directory walking and file parsing */
|
||||
struct baseline_parse_ctx {
|
||||
/* context for directory walking */
|
||||
struct dir_context ctx;
|
||||
/* entry to store the filenames in directory */
|
||||
struct list_head name_list;
|
||||
struct dim_measure *m;
|
||||
baseline_match_func match;
|
||||
baseline_add_func add;
|
||||
};
|
||||
|
||||
/* function implemented to parse the static baseline file in complex format */
|
||||
int baseline_parse_complex_format(char *buf, size_t buf_len,
|
||||
struct baseline_parse_ctx *ctx);
|
||||
#define dim_baseline_parse baseline_parse_complex_format
|
||||
|
||||
/* build or rebuild the static baseline into the measurement handle */
|
||||
int dim_core_static_baseline_load(struct dim_measure *m);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dim_utils.h"
|
||||
#include "dim_core_static_baseline.h"
|
||||
|
||||
#define DIM_STATIC_BASELINE_LINE_MAX 10000
|
||||
|
||||
#define DIM_STATIC_BASELINE_PREFIX "dim"
|
||||
/* dim KERNEL sha256:{digest} {PATH_MAX}\n*/
|
||||
#define DIM_STATIC_BASELINE_LEN_MAX (strlen(DIM_STATIC_BASELINE_PREFIX) + 1 + \
|
||||
NAME_MAX + 1 + NAME_MAX + 1 + \
|
||||
PATH_MAX + 1 + 1)
|
||||
|
||||
static int parse_line(char* line, int line_no, void *data)
|
||||
{
|
||||
int type = 0;
|
||||
size_t len = 0;
|
||||
char *p = NULL;
|
||||
char *line_str = line;
|
||||
struct dim_digest digest = { 0 };
|
||||
struct baseline_parse_ctx *ctx = data;
|
||||
|
||||
if (line_no > DIM_STATIC_BASELINE_LINE_MAX) {
|
||||
dim_warn("more than %d baseline items will be ignored\n",
|
||||
DIM_STATIC_BASELINE_LINE_MAX);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (strlen(line) == 0 || line[0] == '#')
|
||||
return 0; /* ignore blank line and comment */
|
||||
|
||||
if (strlen(line) > DIM_STATIC_BASELINE_LEN_MAX) {
|
||||
dim_err("overlength item at line %d\n", line_no);
|
||||
return 0; /* ignore baseline parsing failed */
|
||||
}
|
||||
|
||||
if ((p = strsep(&line_str, " ")) == NULL ||
|
||||
dim_strcmp(p, DIM_STATIC_BASELINE_PREFIX) != 0) {
|
||||
dim_warn("invalid baseline prefix at line %d\n", line_no);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((p = strsep(&line_str, " ")) == NULL ||
|
||||
(type = dim_baseline_get_type(p)) == DIM_BASELINE_LAST) {
|
||||
dim_warn("invalid baseline type at line %d\n", line_no);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((p = strsep(&line_str, ":")) == NULL ||
|
||||
(digest.algo = dim_hash_algo(p)) == HASH_ALGO__LAST) {
|
||||
dim_warn("invalid baseline algo at line %d\n", line_no);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((p = strsep(&line_str, " ")) == NULL ||
|
||||
strlen(p) != (dim_digest_size(digest.algo) << 1) ||
|
||||
hex2bin(digest.data, p, dim_digest_size(digest.algo)) < 0) {
|
||||
dim_warn("invalid baseline digest at line %d\n", line_no);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (line_str == NULL) {
|
||||
dim_warn("no baseline name at line %d\n", line_no);
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = strlen(line_str);
|
||||
if (len == 0 || len > PATH_MAX) {
|
||||
dim_warn("invalid baseline name at line %d\n", line_no);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ctx->match(line_str, type))
|
||||
return 0;
|
||||
|
||||
return ctx->add(line_str, type, &digest, ctx->m);
|
||||
}
|
||||
|
||||
int baseline_parse_complex_format(char *buf, size_t buf_len,
|
||||
struct baseline_parse_ctx *ctx)
|
||||
{
|
||||
if (buf == NULL || buf_len == 0 || ctx == NULL || ctx->m == NULL ||
|
||||
ctx->match == NULL || ctx->add == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return dim_parse_line_buf(buf, buf_len, parse_line, ctx);
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
#include "dim_core_symbol.h"
|
||||
#include "dim_core_measure.h"
|
||||
#include "dim_core_policy.h"
|
||||
|
||||
#include "dim_core_measure_task.h"
|
||||
|
||||
/* max size of x86 */
|
||||
#define DIM_JUMP_LABEL_NOP_SIZE_MAX 5
|
||||
|
||||
static int code_cmp(const void *a, const void *b)
|
||||
{
|
||||
return *(unsigned long *)a > *(unsigned long *)b ? 1 : 0;
|
||||
}
|
||||
|
||||
static int sort_jump_table(struct jump_entry *sjump,
|
||||
unsigned int jump_counts,
|
||||
unsigned long **code)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned long *buf = NULL;
|
||||
|
||||
buf = dim_vzalloc(sizeof(unsigned long) * jump_counts);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dim_core_kernel_symbol.jump_label_lock();
|
||||
for (i = 0; i < jump_counts; i++)
|
||||
buf[i] = jump_entry_code(&sjump[i]);
|
||||
dim_core_kernel_symbol.jump_label_unlock();
|
||||
|
||||
sort(buf, jump_counts, sizeof(unsigned long), code_cmp, NULL);
|
||||
*code = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_calc_kernel_digest(uintptr_t saddr,
|
||||
uintptr_t eaddr,
|
||||
uintptr_t *jcode_sort,
|
||||
unsigned int jcode_cnt,
|
||||
struct dim_hash *hash,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i;
|
||||
uintptr_t jump_code;
|
||||
uintptr_t cur_addr = saddr;
|
||||
SHASH_DESC_ON_STACK(shash, hash->tfm);
|
||||
|
||||
shash->tfm = hash->tfm;
|
||||
if (shash->tfm == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ret = crypto_shash_init(shash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* if jump label is not enabled, jcode_cnt is 0 */
|
||||
for (i = 0; i < jcode_cnt && cur_addr < eaddr; i++) {
|
||||
jump_code = jcode_sort[i];
|
||||
if (jump_code < cur_addr) /* jump_code can be 0 */
|
||||
continue;
|
||||
|
||||
if (jump_code >= eaddr) /* no more valid jump code */
|
||||
break;
|
||||
|
||||
/* skip addresses that may be changed */
|
||||
if (jump_code > cur_addr) {
|
||||
ret = crypto_shash_update(shash, (char *)cur_addr,
|
||||
jump_code - cur_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
#ifdef JUMP_LABEL_NOP_SIZE
|
||||
cur_addr = jump_code + JUMP_LABEL_NOP_SIZE;
|
||||
#else
|
||||
cur_addr = jump_code + DIM_JUMP_LABEL_NOP_SIZE_MAX;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cur_addr < eaddr) {
|
||||
ret = crypto_shash_update(shash, (char *)cur_addr,
|
||||
eaddr - cur_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return crypto_shash_final(shash, digest->data);
|
||||
}
|
||||
|
||||
static int calc_kernel_digest(struct dim_hash *hash, struct dim_digest *digest)
|
||||
{
|
||||
int ret = 0;
|
||||
uintptr_t stext = 0;
|
||||
uintptr_t etext = 0;
|
||||
struct jump_entry *sjump = NULL;
|
||||
struct jump_entry *ejump = NULL;
|
||||
uintptr_t *jcode_sort = NULL;
|
||||
unsigned int jcode_cnt = 0;
|
||||
|
||||
stext = (uintptr_t)dim_core_kernel_symbol.stext;
|
||||
etext = (uintptr_t)dim_core_kernel_symbol.etext;
|
||||
sjump = dim_core_kernel_symbol.start_jump_table;
|
||||
ejump = dim_core_kernel_symbol.stop_jump_table;
|
||||
if (sjump != NULL && ejump != NULL && sjump < ejump) {
|
||||
jcode_cnt = ((uintptr_t)ejump - (uintptr_t)sjump) /
|
||||
sizeof(struct jump_entry);
|
||||
ret = sort_jump_table(sjump, jcode_cnt, &jcode_sort);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to sort kernel jump table: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
jcode_sort = NULL;
|
||||
jcode_cnt = 0;
|
||||
}
|
||||
|
||||
ret = do_calc_kernel_digest(stext, etext, jcode_sort,
|
||||
jcode_cnt, hash, digest);
|
||||
if (ret < 0)
|
||||
dim_err("failed to calculate kernel digest: %d\n", ret);
|
||||
|
||||
dim_vfree(jcode_sort);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kernel_text_measure(int mode, struct dim_measure *m)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *kr = init_uts_ns.name.release;
|
||||
struct dim_digest digest = {0};
|
||||
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
digest.algo = m->hash.algo;
|
||||
|
||||
if (!dim_core_policy_match(DIM_POLICY_OBJ_KERNEL_TEXT,
|
||||
DIM_POLICY_KEY_NAME, kr))
|
||||
return 0;
|
||||
|
||||
ret = calc_kernel_digest(&m->hash, &digest);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to calculate kernel digest: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dim_measure_process_dynamic_result(m, mode, kr, &digest, NULL);
|
||||
if (ret < 0)
|
||||
dim_err("failed to check kernel digest: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct dim_measure_task dim_core_measure_task_kernel_text = {
|
||||
.name = "dim_core_measure_task_kernel_text",
|
||||
.init = NULL,
|
||||
.destroy = NULL,
|
||||
.measure = kernel_text_measure,
|
||||
};
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "dim_utils.h"
|
||||
#include "dim_hash.h"
|
||||
#include "dim_baseline.h"
|
||||
#include "dim_measure_log.h"
|
||||
|
||||
#include "dim_core_measure.h"
|
||||
#include "dim_core_policy.h"
|
||||
#include "dim_core_symbol.h"
|
||||
|
||||
#include "dim_core_measure_task.h"
|
||||
|
||||
struct module_text_measure_ctx {
|
||||
struct dim_measure *m;
|
||||
int mode;
|
||||
};
|
||||
|
||||
static int calculate_module_digest(const char *name,
|
||||
struct dim_hash *hash,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
int ret = 0;
|
||||
struct module *mod = NULL;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
mutex_lock(&module_mutex);
|
||||
mod = find_module(name);
|
||||
#else
|
||||
rcu_read_lock_sched();
|
||||
mod = dim_core_kernel_symbol.find_module(name);
|
||||
#endif
|
||||
if (mod == NULL || mod->state != MODULE_STATE_LIVE ||
|
||||
!try_module_get(mod))
|
||||
mod = NULL; /* target module not exist or is not alive */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
mutex_unlock(&module_mutex);
|
||||
#else
|
||||
rcu_read_unlock_sched();
|
||||
#endif
|
||||
if (mod == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
ret = dim_hash_calculate(mod->core_layout.base,
|
||||
mod->core_layout.text_size,
|
||||
hash, digest);
|
||||
#else
|
||||
ret = dim_hash_calculate(mod->mem[MOD_TEXT].base,
|
||||
mod->mem[MOD_TEXT].size,
|
||||
hash, digest);
|
||||
#endif
|
||||
module_put(mod);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int measure_module(struct dim_policy *policy, void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct module_text_measure_ctx *ctx = data;
|
||||
const char *mod_name = NULL;
|
||||
struct dim_digest digest = { 0 };
|
||||
|
||||
if (policy == NULL || policy->obj != DIM_POLICY_OBJ_MODULE_TEXT ||
|
||||
policy->name == NULL)
|
||||
return 0;
|
||||
|
||||
mod_name = policy->name;
|
||||
|
||||
/* if module is not inserted in baseline_init stage, ignore it */
|
||||
if (ctx->mode == DIM_MEASURE &&
|
||||
dim_measure_dynamic_baseline_search(ctx->m, mod_name,
|
||||
DIM_BASELINE_KERNEL, &digest) < 0)
|
||||
return 0;
|
||||
|
||||
digest.algo = ctx->m->hash.algo;
|
||||
ret = calculate_module_digest(mod_name, &ctx->m->hash, &digest);
|
||||
if (ret < 0) {
|
||||
dim_err("fail to calculate digest of module %s: %d\n",
|
||||
mod_name, ret);
|
||||
return ret == -ENOENT ? 0 : ret;
|
||||
}
|
||||
|
||||
ret = dim_measure_process_dynamic_result(ctx->m, ctx->mode,
|
||||
mod_name, &digest, NULL);
|
||||
if (ret < 0)
|
||||
dim_err("failed to check module digest: %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int module_text_measure(int mode, struct dim_measure *m)
|
||||
{
|
||||
struct module_text_measure_ctx ctx = {
|
||||
.m = m,
|
||||
.mode = mode,
|
||||
};
|
||||
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return dim_core_policy_walk(measure_module, &ctx);
|
||||
}
|
||||
|
||||
struct dim_measure_task dim_core_measure_task_module_text = {
|
||||
.name = "dim_core_measure_task_module_text",
|
||||
.init = NULL,
|
||||
.destroy = NULL,
|
||||
.measure = module_text_measure,
|
||||
};
|
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "dim_hash.h"
|
||||
#include "dim_measure_log.h"
|
||||
#include "dim_baseline.h"
|
||||
|
||||
#include "dim_core_symbol.h"
|
||||
#include "dim_core_policy.h"
|
||||
#include "dim_core_measure.h"
|
||||
|
||||
#include "dim_core_measure_task.h"
|
||||
#include "dim_core_measure_process.h"
|
||||
|
||||
static struct vm_area_struct *next_module_text_vma(struct vm_area_struct *vma)
|
||||
{
|
||||
struct vm_area_struct *v = NULL;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
for (v = vma->vm_next; v != NULL &&
|
||||
!(vma_is_file_text(v) && !vma_file_is_same(v, vma));
|
||||
v = v->vm_next) {}
|
||||
#else
|
||||
VMA_ITERATOR(vmi, vma->vm_mm, vma->vm_end);
|
||||
for_each_vma(vmi, v) {
|
||||
if (vma_is_file_text(v) && !vma_file_is_same(v, vma))
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
|
||||
static int kill_task(struct task_struct *p, void * __always_unused data)
|
||||
{
|
||||
if (p == current) {
|
||||
/* dont kill the current process */
|
||||
dim_warn("don't kill the current process\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
send_sig(SIGKILL, p, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kill_task_tree(struct task_struct *tsk)
|
||||
{
|
||||
if (tsk->pid == 1) {
|
||||
/* dont kill the init process */
|
||||
dim_warn("the pid of tampered task is 1, don't kill it\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tsk == current) {
|
||||
/* dont kill the current process */
|
||||
dim_warn("don't kill the current process\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dim_core_kernel_symbol.walk_process_tree(tsk, kill_task, NULL);
|
||||
send_sig(SIGKILL, tsk, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool vm_file_match_policy(struct file *vm_file,
|
||||
struct task_measure_ctx *ctx)
|
||||
{
|
||||
struct dim_digest dig = { 0 };
|
||||
|
||||
/* get the module path string */
|
||||
ctx->path = d_path(&vm_file->f_path, ctx->path_buf, PATH_MAX);
|
||||
if (IS_ERR(ctx->path)) {
|
||||
dim_warn("failed to get path of vma: %ld\n", PTR_ERR(ctx->path));
|
||||
ctx->path = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctx->mode == DIM_BASELINE)
|
||||
return dim_core_policy_match(DIM_POLICY_OBJ_BPRM_TEXT,
|
||||
DIM_POLICY_KEY_PATH, ctx->path);
|
||||
|
||||
return dim_measure_dynamic_baseline_search(ctx->m, ctx->path,
|
||||
DIM_BASELINE_USER, &dig) == 0;
|
||||
}
|
||||
|
||||
static int check_process_digest(struct dim_digest *digest,
|
||||
void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
int log_flag = 0;
|
||||
int action = 0;
|
||||
struct task_measure_ctx *ctx = data;
|
||||
|
||||
if (digest == NULL || data == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ret = dim_measure_process_static_result(ctx->m, ctx->mode, ctx->path,
|
||||
digest, &log_flag);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to check user digest: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (log_flag != LOG_TAMPERED || ctx->mode != DIM_MEASURE ||
|
||||
dim_core_measure_action_get() == DIM_MEASURE_ACTION_DISABLE)
|
||||
return 0;
|
||||
|
||||
/* now the process is tampered, check if action need to be taken */
|
||||
action = dim_core_policy_get_action(DIM_POLICY_OBJ_BPRM_TEXT,
|
||||
DIM_POLICY_KEY_PATH, ctx->path);
|
||||
if (action == DIM_POLICY_ACTION_KILL) {
|
||||
dim_warn("kill action is set, process %s will be killed\n",
|
||||
ctx->path);
|
||||
ctx->task_kill = true; /* this task need to be killed */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void measure_task_module(struct vm_area_struct *vma,
|
||||
struct task_measure_ctx *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* vma is the first file mapping text vma of the module,
|
||||
so vm_file is not NULL */
|
||||
if (!vm_file_match_policy(get_vm_file(vma), ctx))
|
||||
return; /* no need to measure */
|
||||
|
||||
ctx->task_measure = true;
|
||||
|
||||
/* now we only measure the text memory */
|
||||
ret = measure_process_text(vma, ctx);
|
||||
if (ret < 0)
|
||||
dim_err("failed to measure module file text: %d", ret);
|
||||
}
|
||||
|
||||
static int measure_task(struct task_struct *task, struct task_measure_ctx *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mm_struct *mm = NULL;
|
||||
struct vm_area_struct *v = NULL;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||
struct vma_iterator vmi = { 0 };
|
||||
#endif
|
||||
mm = get_task_mm(task);
|
||||
if (mm == NULL)
|
||||
return 0;
|
||||
|
||||
ret = down_read_killable(&mm->mmap_lock);
|
||||
if (ret < 0) {
|
||||
mmput(mm);
|
||||
return ret; /* need to return if killed */
|
||||
}
|
||||
|
||||
ctx->path = NULL;
|
||||
ctx->task = task;
|
||||
ctx->task_kill = false;
|
||||
ctx->task_measure = false;
|
||||
|
||||
/* find the first file mapping text vma, which is the
|
||||
start vma of a module */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
for (v = mm->mmap; v != NULL &&
|
||||
!vma_is_file_text(v); v = v->vm_next) {}
|
||||
#else
|
||||
vma_iter_init(&vmi, mm, 0);
|
||||
for_each_vma(vmi, v) {
|
||||
if (vma_is_file_text(v))
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (v == NULL) {
|
||||
dim_warn("no valid file text vma");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (; v != NULL; v = next_module_text_vma(v))
|
||||
measure_task_module(v, ctx);
|
||||
out:
|
||||
up_read(&mm->mmap_lock);
|
||||
mmput(mm);
|
||||
|
||||
if (ctx->task_kill) {
|
||||
ret = kill_task_tree(task);
|
||||
if (ret < 0)
|
||||
dim_err("failed to kill tampered task, pid = %d: %d\n",
|
||||
task->pid, ret);
|
||||
}
|
||||
|
||||
/* do schedule if this task is measured */
|
||||
if (ctx->task_measure)
|
||||
dim_measure_schedule(ctx->m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int store_task_pids(pid_t **pid_buf, unsigned int *pid_cnt)
|
||||
{
|
||||
struct task_struct *tsk = NULL;
|
||||
pid_t *buf = NULL;
|
||||
unsigned int cnt = 0;
|
||||
unsigned int max_cnt = (PID_MAX_DEFAULT << 1);
|
||||
|
||||
/* maximum processing of PID_MAX_DEFAULT * 2 pids */
|
||||
buf = dim_vzalloc(max_cnt);
|
||||
if (buf == NULL) {
|
||||
dim_err("failed to allocate memory for pid buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cnt = 0;
|
||||
rcu_read_lock();
|
||||
for_each_process(tsk) {
|
||||
/* don't measure kernel thread */
|
||||
if (tsk->flags & PF_KTHREAD)
|
||||
continue;
|
||||
|
||||
buf[cnt++] = tsk->pid;
|
||||
if (cnt >= max_cnt) {
|
||||
dim_warn("pid number reaches the limit\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
*pid_buf = buf;
|
||||
*pid_cnt = cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int walk_measure_tasks(struct task_measure_ctx *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i = 0;
|
||||
unsigned int pid_cnt = 0;
|
||||
pid_t *pid_buf = NULL;
|
||||
struct task_struct *task = NULL;
|
||||
|
||||
ret = store_task_pids(&pid_buf, &pid_cnt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < pid_cnt; i++) {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
task = find_get_task_by_vpid(pid_buf[i]);
|
||||
#else
|
||||
task = dim_core_kernel_symbol.find_get_task_by_vpid(pid_buf[i]);
|
||||
#endif
|
||||
if (task == NULL)
|
||||
continue;
|
||||
|
||||
ret = measure_task(task, ctx);
|
||||
put_task_struct(task);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to measure task, pid = %d: %d", pid_buf[i], ret);
|
||||
if (ret == -EINTR)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dim_vfree(pid_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int user_text_measure(int mode, struct dim_measure *m)
|
||||
{
|
||||
int ret = 0;
|
||||
struct task_measure_ctx *ctx = NULL;
|
||||
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ctx = dim_vzalloc(sizeof(struct task_measure_ctx));
|
||||
if (ctx == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->mode = mode;
|
||||
ctx->m = m;
|
||||
ctx->check = check_process_digest;
|
||||
|
||||
ret = walk_measure_tasks(ctx);
|
||||
dim_vfree(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct dim_measure_task dim_core_measure_task_user_text = {
|
||||
.name = "dim_core_measure_task_user_text",
|
||||
.measure = user_text_measure,
|
||||
};
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_CORE_MEASURE_PROCESS_H
|
||||
#define __DIM_CORE_MEASURE_PROCESS_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
/* callback funtion to check results when do measurement */
|
||||
typedef int (*process_digest_check_func) (struct dim_digest *digest,
|
||||
void *ctx);
|
||||
|
||||
/* the context used in user process measurement */
|
||||
struct task_measure_ctx {
|
||||
struct dim_measure *m;
|
||||
/* DIM_BASELINE or DIM_MEASURE */
|
||||
int mode;
|
||||
char path_buf[PATH_MAX];
|
||||
/* current measured process name */
|
||||
const char *path;
|
||||
/* current measured process */
|
||||
struct task_struct *task;
|
||||
/* this process need to be killed */
|
||||
bool task_kill;
|
||||
/* this process is measured */
|
||||
bool task_measure;
|
||||
/* check function */
|
||||
process_digest_check_func check;
|
||||
};
|
||||
|
||||
static inline struct file *get_vm_file(struct vm_area_struct *vma)
|
||||
{
|
||||
return vma == NULL ? NULL : vma->vm_file;
|
||||
}
|
||||
|
||||
static inline bool vma_is_text(struct vm_area_struct *vma)
|
||||
{
|
||||
return (vma->vm_flags & VM_READ) && (vma->vm_flags & VM_EXEC) &&
|
||||
!(vma->vm_flags & VM_WRITE);
|
||||
}
|
||||
|
||||
static inline bool vma_is_file_text(struct vm_area_struct *vma)
|
||||
{
|
||||
return vma_is_text(vma) && get_vm_file(vma) != NULL;
|
||||
}
|
||||
|
||||
static inline bool vma_file_is_same(struct vm_area_struct *first,
|
||||
struct vm_area_struct *second)
|
||||
{
|
||||
return get_vm_file(first) == get_vm_file(second);
|
||||
}
|
||||
|
||||
static inline bool vma_can_merge(struct vm_area_struct *first,
|
||||
struct vm_area_struct *second)
|
||||
{
|
||||
return (first->vm_end == second->vm_start) &&
|
||||
(vma_file_is_same(first, second));
|
||||
}
|
||||
|
||||
static inline bool vma_is_not_same_module(struct vm_area_struct *a,
|
||||
struct vm_area_struct *b)
|
||||
{
|
||||
struct file *fa = get_vm_file(a);
|
||||
struct file *fb = get_vm_file(b);
|
||||
return (fa != NULL && fb != NULL && fa != fb);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DIM_CORE_MEASURE_PROCESS_ELF
|
||||
int measure_process_module_text_elf(struct vm_area_struct *vma,
|
||||
struct task_measure_ctx *ctx);
|
||||
#define measure_process_text measure_process_module_text_elf
|
||||
#else
|
||||
int measure_process_module_text_vma(struct vm_area_struct *vma,
|
||||
struct task_measure_ctx *ctx);
|
||||
#define measure_process_text measure_process_module_text_vma
|
||||
#endif
|
||||
|
||||
extern struct dim_measure_task dim_core_measure_task_user_text;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include "dim_measure.h"
|
||||
#include "dim_vm_hash.h"
|
||||
#include "dim_core_measure_process.h"
|
||||
|
||||
static struct vm_area_struct *find_text_vma_end(struct vm_area_struct *vma)
|
||||
{
|
||||
struct vm_area_struct *v = NULL;
|
||||
struct vm_area_struct *vma_end = vma;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
for (v = vma->vm_next; v != NULL && vma_is_file_text(v) &&
|
||||
vma_can_merge(vma_end, v); v = v->vm_next)
|
||||
vma_end = v;
|
||||
#else
|
||||
VMA_ITERATOR(vmi, vma->vm_mm, vma->vm_end);
|
||||
for_each_vma(vmi, v) {
|
||||
if (!vma_is_file_text(v) || !vma_can_merge(vma_end, v))
|
||||
break;
|
||||
|
||||
vma_end = v;
|
||||
}
|
||||
#endif
|
||||
return vma_end;
|
||||
}
|
||||
|
||||
static struct vm_area_struct *next_file_text_vma(struct vm_area_struct *vma)
|
||||
{
|
||||
struct vm_area_struct *v = NULL;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
for (v = vma->vm_next; v != NULL &&
|
||||
!vma_is_file_text(v); v = v->vm_next) {}
|
||||
#else
|
||||
VMA_ITERATOR(vmi, vma->vm_mm, vma->vm_end);
|
||||
for_each_vma(vmi, v) {
|
||||
if (vma_is_file_text(v))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!vma_is_file_text(v))
|
||||
v = NULL;
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
|
||||
/* For file text segment, merge all file mapping text vma and measure */
|
||||
int measure_text_vma(struct vm_area_struct *vma, struct task_measure_ctx *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
struct vm_area_struct *v = vma;
|
||||
struct vm_area_struct *v_end = NULL;
|
||||
struct dim_digest digest = {
|
||||
.algo = ctx->m->hash.algo
|
||||
};
|
||||
SHASH_DESC_ON_STACK(shash, ctx->m->hash.tfm);
|
||||
|
||||
shash->tfm = ctx->m->hash.tfm;
|
||||
ret = crypto_shash_init(shash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* now the vma is the first file text vma of a process module */
|
||||
while (v != NULL && vma_file_is_same(v, vma)) {
|
||||
v_end = find_text_vma_end(v);
|
||||
/* update all the continuous text vma */
|
||||
ret = dim_vm_hash_update_vmas(v, v_end, shash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
v = next_file_text_vma(v_end);
|
||||
}
|
||||
|
||||
ret = crypto_shash_final(shash, digest.data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ctx->check(&digest, ctx);
|
||||
}
|
||||
|
||||
int measure_process_module_text_vma(struct vm_area_struct *vma,
|
||||
struct task_measure_ctx *ctx)
|
||||
{
|
||||
if (vma == NULL || !vma_is_file_text(vma) || ctx == NULL
|
||||
|| ctx->m == NULL || ctx->check == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return measure_text_vma(vma, ctx);
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/highmem.h>
|
||||
|
||||
#include "dim_utils.h"
|
||||
#include "dim_safe_func.h"
|
||||
|
||||
#include "dim_vm_hash.h"
|
||||
|
||||
int dim_vm_hash_update_address(struct mm_struct *mm,
|
||||
unsigned long addr_start,
|
||||
unsigned long addr_len,
|
||||
struct shash_desc *shash)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long i = 0;
|
||||
long ret_pages = 0;
|
||||
void *page_ptr = NULL;
|
||||
struct page **pages = NULL;
|
||||
unsigned int update_size = PAGE_SIZE;
|
||||
unsigned long nr_pages = DIV_ROUND_UP(addr_len, PAGE_SIZE);
|
||||
|
||||
if (mm == NULL || addr_len == 0 || shash == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
pages = dim_vzalloc(nr_pages * sizeof(struct page *));
|
||||
if (pages == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret_pages = get_user_pages_remote(mm, addr_start, nr_pages,
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(6,4,0)
|
||||
0, pages, NULL, NULL);
|
||||
#else
|
||||
0, pages, NULL);
|
||||
#endif
|
||||
if (ret_pages < 0) {
|
||||
dim_err("failed to get remote pages: %ld\n", ret_pages);
|
||||
dim_vfree(pages);
|
||||
return ret_pages;
|
||||
} else if (ret_pages != nr_pages) {
|
||||
dim_warn("failed to get all remote pages\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < ret_pages; i++) {
|
||||
page_ptr = kmap(pages[i]);
|
||||
if (page_ptr == NULL) {
|
||||
dim_err("failed to kmap remote page\n");
|
||||
put_page(pages[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == ret_pages - 1)
|
||||
update_size = addr_len % PAGE_SIZE ?
|
||||
addr_len % PAGE_SIZE : PAGE_SIZE;
|
||||
|
||||
ret = crypto_shash_update(shash, page_ptr, update_size);
|
||||
if (ret < 0)
|
||||
dim_warn("failed to update hash: %d\n", ret);
|
||||
|
||||
kunmap(pages[i]);
|
||||
put_page(pages[i]);
|
||||
}
|
||||
|
||||
dim_vfree(pages);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* calculate hash digest of continuous vma */
|
||||
int dim_vm_hash_update_vmas(struct vm_area_struct *vma_start,
|
||||
struct vm_area_struct *vma_end,
|
||||
struct shash_desc *shash)
|
||||
{
|
||||
if (vma_start == NULL || vma_end == NULL || shash == NULL ||
|
||||
vma_start->vm_mm != vma_end->vm_mm ||
|
||||
vma_start->vm_start >= vma_end->vm_end)
|
||||
return -EINVAL;
|
||||
|
||||
return dim_vm_hash_update_address(vma_start->vm_mm, vma_start->vm_start,
|
||||
vma_end->vm_end - vma_start->vm_start, shash);
|
||||
}
|
||||
|
||||
/* calculate hash digest of vma */
|
||||
int dim_vm_hash_calculate_vma(struct vm_area_struct *vma,
|
||||
struct dim_hash *hash,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
int ret = 0;
|
||||
/* check here to avoid code check warning */
|
||||
SHASH_DESC_ON_STACK(shash, hash == NULL ? NULL : hash->tfm);
|
||||
|
||||
if (vma == NULL || hash == NULL || digest == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
shash->tfm = hash->tfm;
|
||||
ret = crypto_shash_init(shash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = dim_vm_hash_update_vmas(vma, vma, shash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return crypto_shash_final(shash, digest->data);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_VM_HASH_H
|
||||
#define __DIM_VM_HASH_H
|
||||
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include "dim_hash.h"
|
||||
|
||||
int dim_vm_hash_update_address(struct mm_struct *mm,
|
||||
unsigned long addr_start,
|
||||
unsigned long addr_len,
|
||||
struct shash_desc *shash);
|
||||
|
||||
int dim_vm_hash_update_vmas(struct vm_area_struct *vma_start,
|
||||
struct vm_area_struct *vma_end,
|
||||
struct shash_desc *shash);
|
||||
|
||||
int dim_vm_hash_calculate_vma(struct vm_area_struct *vma,
|
||||
struct dim_hash *hash,
|
||||
struct dim_digest *digest);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef __DIM_CORE_MEASURE_TASK_H
|
||||
#define __DIM_CORE_MEASURE_TASK_H
|
||||
|
||||
#include "dim_core_measure_process.h"
|
||||
|
||||
extern struct dim_measure_task dim_core_measure_task_module_text;
|
||||
extern struct dim_measure_task dim_core_measure_task_kernel_text;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
static int cfg_check(struct dim_measure_cfg *cfg)
|
||||
{
|
||||
if (cfg->log_cap < MEASURE_LOG_CAP_MIN ||
|
||||
cfg->log_cap > MEASURE_LOG_CAP_MAX) {
|
||||
dim_err("invalid log capacity: %d\n", cfg->log_cap);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
if (cfg->schedule_ms > MEASURE_SCHEDULE_MAX) {
|
||||
dim_err("invalid measure schedule: %d\n", cfg->schedule_ms);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
if (cfg->pcr > DIM_PCR_MAX) {
|
||||
dim_err("invalid TPM pcr number: %d\n", cfg->pcr);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dim_measure_init(struct dim_measure *m, struct dim_measure_cfg *cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (m == NULL || cfg == NULL || cfg_check(cfg) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
INIT_LIST_HEAD(&m->task_list);
|
||||
|
||||
/* 1. init hash algorithm */
|
||||
ret = dim_hash_init(cfg->alg_name, &m->hash);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to init hash algorithm: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* 2. init TPM, dont break if init fail */
|
||||
if (cfg->pcr > 0) {
|
||||
ret = dim_tpm_init(&m->tpm, HASH_ALGO_SHA256);
|
||||
if (ret < 0) {
|
||||
cfg->pcr = 0;
|
||||
dim_warn("failed to init tpm chip: %d\n", ret);
|
||||
}
|
||||
} else {
|
||||
memset(&m->tpm, 0, sizeof(struct dim_tpm));
|
||||
}
|
||||
|
||||
/* 3. init baseline data (static and dynamic) */
|
||||
ret = dim_baseline_init_tree(cfg->sta_malloc, cfg->sta_free,
|
||||
&m->static_baseline);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to init static baseline root: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = dim_baseline_init_tree(cfg->dyn_malloc, cfg->dyn_free,
|
||||
&m->dynamic_baseline);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to init dynamic baseline root: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* 4. init measure log */
|
||||
ret = dim_measure_log_init_tree(&m->log, &m->hash, &m->tpm,
|
||||
cfg->log_cap, cfg->pcr);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to init measure log: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* 5. set measure schedule time */
|
||||
m->schedule_jiffies = cfg->schedule_ms == 0 ? 0 :
|
||||
msecs_to_jiffies(cfg->schedule_ms);
|
||||
|
||||
/* 6. set initial status */
|
||||
atomic_set(&m->status, MEASURE_STATUS_NO_BASELINE);
|
||||
return 0;
|
||||
err:
|
||||
dim_measure_destroy(m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dim_measure_destroy(struct dim_measure *m)
|
||||
{
|
||||
if (m == NULL)
|
||||
return;
|
||||
|
||||
mutex_lock(&m->measure_lock);
|
||||
dim_measure_tasks_unregister_all(m);
|
||||
dim_measure_log_destroy_tree(&m->log);
|
||||
dim_baseline_destroy_tree(&m->static_baseline);
|
||||
dim_baseline_destroy_tree(&m->dynamic_baseline);
|
||||
dim_tpm_destroy(&m->tpm);
|
||||
dim_hash_destroy(&m->hash);
|
||||
mutex_unlock(&m->measure_lock);
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_MEASURE_H
|
||||
#define __DIM_MEASURE_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "dim_baseline.h"
|
||||
#include "dim_hash.h"
|
||||
#include "dim_measure_log.h"
|
||||
#include "dim_tpm.h"
|
||||
#include "dim_utils.h"
|
||||
|
||||
#define DIM_MEASURE 0
|
||||
#define DIM_BASELINE 1
|
||||
|
||||
/* limit of measure parameter */
|
||||
#define MEASURE_LOG_CAP_MAX (UINT_MAX)
|
||||
#define MEASURE_LOG_CAP_MIN (100)
|
||||
#define MEASURE_SCHEDULE_MAX (1000)
|
||||
|
||||
/* status of measurement */
|
||||
enum dim_measure_status {
|
||||
MEASURE_STATUS_OFF,
|
||||
MEASURE_STATUS_NO_BASELINE,
|
||||
MEASURE_STATUS_BASELINE_RUNNING,
|
||||
MEASURE_STATUS_MEASURE_RUNNING,
|
||||
MEASURE_STATUS_PROTECTED,
|
||||
MEASURE_STATUS_ERROR,
|
||||
MEASURE_STATUS_LAST,
|
||||
};
|
||||
|
||||
/* the common configuration for measurement */
|
||||
struct dim_measure_cfg {
|
||||
/* hash algorithm for measurement */
|
||||
char *alg_name;
|
||||
/* schedule time (ms) after one valid measurement */
|
||||
unsigned int schedule_ms;
|
||||
/* PCR number for TPM extending */
|
||||
unsigned int pcr;
|
||||
/* max measure log number */
|
||||
unsigned int log_cap;
|
||||
/* memory function for baseline store */
|
||||
malloc_func dyn_malloc;
|
||||
free_func dyn_free;
|
||||
malloc_func sta_malloc;
|
||||
free_func sta_free;
|
||||
};
|
||||
|
||||
/* the dim measurement global handle */
|
||||
struct dim_measure {
|
||||
/* schedule time (jittfies) after one valid measurement */
|
||||
unsigned long schedule_jiffies;
|
||||
/* lock to prevent concurrent measurement */
|
||||
struct mutex measure_lock;
|
||||
/* measure hash algorithm */
|
||||
struct dim_hash hash;
|
||||
/* TPM chip handle */
|
||||
struct dim_tpm tpm;
|
||||
/* measure log */
|
||||
struct dim_measure_log_tree log;
|
||||
/* measure baseline */
|
||||
struct dim_baseline_tree static_baseline;
|
||||
struct dim_baseline_tree dynamic_baseline;
|
||||
/* function called before doing baseline */
|
||||
int (*baseline_prepare)(struct dim_measure *m);
|
||||
/* measure status */
|
||||
atomic_t status;
|
||||
/* task list */
|
||||
struct list_head task_list;
|
||||
};
|
||||
|
||||
/* the task definition for measurement function */
|
||||
struct dim_measure_task {
|
||||
struct list_head node;
|
||||
/* task name for log printing */
|
||||
const char *name;
|
||||
/* init and destroy functions */
|
||||
int (*init)(void);
|
||||
void (*destroy)(void);
|
||||
/* measure function */
|
||||
int (*measure)(int mode, struct dim_measure *m);
|
||||
};
|
||||
|
||||
/* functions for dim measure handle */
|
||||
int dim_measure_init(struct dim_measure *m, struct dim_measure_cfg *cfg);
|
||||
void dim_measure_destroy(struct dim_measure *m);
|
||||
|
||||
/* functions for measurement results processing */
|
||||
int dim_measure_process_static_result(struct dim_measure *m, int mode,
|
||||
const char *name,
|
||||
struct dim_digest *digest,
|
||||
int *log_flag);
|
||||
int dim_measure_process_dynamic_result(struct dim_measure *m, int mode,
|
||||
const char *name,
|
||||
struct dim_digest *digest,
|
||||
int *log_flag);
|
||||
int dim_measure_static_baseline_add(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest);
|
||||
int dim_measure_dynamic_baseline_add(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest);
|
||||
int dim_measure_static_baseline_search(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest);
|
||||
int dim_measure_dynamic_baseline_search(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest);
|
||||
|
||||
/* functions for dim measurement task */
|
||||
int dim_measure_tasks_register(struct dim_measure *m,
|
||||
struct dim_measure_task **tasks,
|
||||
unsigned int num);
|
||||
void dim_measure_tasks_unregister_all(struct dim_measure *m);
|
||||
void dim_measure_task_measure(int mode, struct dim_measure *m);
|
||||
|
||||
/* functions for dim measurement status */
|
||||
const char *dim_measure_status_print(struct dim_measure *m);
|
||||
bool dim_measure_status_error(struct dim_measure *m);
|
||||
|
||||
/* tool functions used for implementing measure tasks */
|
||||
void dim_measure_schedule(struct dim_measure *m);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
static inline bool is_valid_mode(int mode)
|
||||
{
|
||||
return mode == DIM_BASELINE || mode == DIM_MEASURE;
|
||||
}
|
||||
|
||||
static const char *process_static_name(const char *name, int type,
|
||||
char *buf, int buf_len)
|
||||
{
|
||||
const char *kr = init_uts_ns.name.release;
|
||||
|
||||
if (type != DIM_BASELINE_KERNEL || dim_strcmp(name, kr) == 0)
|
||||
return name;
|
||||
|
||||
/* name of kernel module has a kernel prefix in static baseline */
|
||||
if (sprintf(buf, "%s/%s", kr, name) < 0)
|
||||
return NULL;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int static_baseline_add(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
int ret = dim_baseline_add(&m->static_baseline, name, type, digest);
|
||||
if (ret < 0 && ret != -EEXIST) {
|
||||
dim_err("failed to add static baseline of %s\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dynamic_baseline_add(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
int ret = dim_baseline_add(&m->dynamic_baseline, name, type, digest);
|
||||
if (ret < 0 && ret != -EEXIST) {
|
||||
dim_err("failed to add dynamic baseline of %s\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool static_baseline_match(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
char buf[NAME_MAX + NAME_MAX + 1 + 1] = { 0 };
|
||||
return dim_baseline_match(&m->static_baseline,
|
||||
process_static_name(name, type, buf, sizeof(buf)),
|
||||
type, digest);
|
||||
}
|
||||
|
||||
static bool dynamic_baseline_match(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
return dim_baseline_match(&m->dynamic_baseline, name, type, digest);
|
||||
}
|
||||
|
||||
static int static_baseline_search(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
char buf[NAME_MAX + NAME_MAX + 1 + 1] = { 0 };
|
||||
return dim_baseline_search_digest(&m->static_baseline,
|
||||
process_static_name(name, type, buf, sizeof(buf)),
|
||||
type, digest);
|
||||
}
|
||||
|
||||
static int dynamic_baseline_search(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
return dim_baseline_search_digest(&m->dynamic_baseline, name,
|
||||
type, digest);
|
||||
}
|
||||
|
||||
static int measure_log_add(struct dim_measure *m, const char *name,
|
||||
struct dim_digest *digest, int flag)
|
||||
{
|
||||
int ret = dim_measure_log_add(&m->log, name, digest, flag);
|
||||
if (ret < 0 && ret != -EEXIST) {
|
||||
dim_err("failed to add measure log of %s: %d\n", name, ret);
|
||||
/* the measure log of this object has been limited */
|
||||
return ret == -ENOSPC ? 0 : ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check dynamic measurement result in baseline stage */
|
||||
static int process_dynamic_baseline(struct dim_measure *m, const char *name,
|
||||
struct dim_digest *digest, int *log_flag)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_digest digest_static = { 0 };
|
||||
int def_flag = log_flag == NULL ? LOG_NO_SATIC_BASELINE : *log_flag;
|
||||
|
||||
if (m == NULL || name == NULL || digest == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* 1. add digest to dynamic baseline */
|
||||
ret = dynamic_baseline_add(m, name, DIM_BASELINE_KERNEL, digest);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* 2. search digest from static baseline */
|
||||
ret = static_baseline_search(m, name, DIM_BASELINE_KERNEL, &digest_static);
|
||||
if (ret < 0)
|
||||
/* 2.1. if not find, log the dynamic baseline */
|
||||
return measure_log_add(m, name, digest, def_flag);
|
||||
|
||||
/* 2.2. if find, log the static baseline */
|
||||
return measure_log_add(m, name, &digest_static, LOG_STATIC_BASELINE);
|
||||
}
|
||||
|
||||
/* process dynamic measurement result in measure stage */
|
||||
static int process_dynamic_measure(struct dim_measure *m, const char *name,
|
||||
struct dim_digest *digest, int *log_flag)
|
||||
{
|
||||
if (m == NULL || name == NULL || digest == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if(!dynamic_baseline_match(m, name, DIM_BASELINE_KERNEL, digest)) {
|
||||
dim_err("mismatch dynamic baseline of kernel %s\n", name);
|
||||
if (log_flag != NULL)
|
||||
*log_flag = LOG_TAMPERED;
|
||||
|
||||
return measure_log_add(m, name, digest, LOG_TAMPERED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* process static measurement result in baseline stage */
|
||||
static int process_static_baseline(struct dim_measure *m, const char *name,
|
||||
struct dim_digest *digest, int *log_flag)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dim_digest digest_static = { 0 };
|
||||
|
||||
/* 1. add digest to dynamic baseline */
|
||||
ret = dynamic_baseline_add(m, name, DIM_BASELINE_USER, digest);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* 2. search digest from static baseline */
|
||||
ret = static_baseline_search(m, name, DIM_BASELINE_USER, &digest_static);
|
||||
if (ret < 0) /* 2.1. if not find, log the dynamic baseline */
|
||||
return measure_log_add(m, name, digest, LOG_NO_SATIC_BASELINE);
|
||||
|
||||
/* 2.2. if find, compare with the static baseline */
|
||||
if (static_baseline_match(m, name, DIM_BASELINE_USER, digest))
|
||||
return measure_log_add(m, name, digest, LOG_STATIC_BASELINE);
|
||||
|
||||
dim_warn("mismatch static baseline of user process %s\n", name);
|
||||
if (log_flag != NULL)
|
||||
*log_flag = LOG_TAMPERED;
|
||||
|
||||
return measure_log_add(m, name, digest, LOG_TAMPERED);
|
||||
}
|
||||
|
||||
/* process static measurement result in measure stage */
|
||||
static int process_static_measure(struct dim_measure *m, const char *name,
|
||||
struct dim_digest *digest, int *log_flag)
|
||||
{
|
||||
if(!dynamic_baseline_match(m, name, DIM_BASELINE_USER, digest)) {
|
||||
dim_err("mismatch dynamic baseline of user %s\n", name);
|
||||
if (log_flag != NULL)
|
||||
*log_flag = LOG_TAMPERED;
|
||||
|
||||
return measure_log_add(m, name, digest, LOG_TAMPERED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dim_measure_process_static_result(struct dim_measure *m, int mode,
|
||||
const char *name,
|
||||
struct dim_digest *digest,
|
||||
int *log_flag)
|
||||
{
|
||||
if (m == NULL || name == NULL || digest == NULL ||
|
||||
!is_valid_mode(mode))
|
||||
return -EINVAL;
|
||||
|
||||
return mode == DIM_BASELINE ?
|
||||
process_static_baseline(m, name, digest, log_flag) :
|
||||
process_static_measure(m, name, digest, log_flag);
|
||||
}
|
||||
|
||||
int dim_measure_process_dynamic_result(struct dim_measure *m, int mode,
|
||||
const char *name,
|
||||
struct dim_digest *digest,
|
||||
int *log_flag)
|
||||
{
|
||||
if (m == NULL || name == NULL || digest == NULL ||
|
||||
!is_valid_mode(mode))
|
||||
return -EINVAL;
|
||||
|
||||
return mode == DIM_BASELINE ?
|
||||
process_dynamic_baseline(m, name, digest, log_flag) :
|
||||
process_dynamic_measure(m, name, digest, log_flag);
|
||||
}
|
||||
|
||||
int dim_measure_static_baseline_add(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return static_baseline_add(m, name, type, digest);
|
||||
}
|
||||
|
||||
int dim_measure_dynamic_baseline_add(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return dynamic_baseline_add(m, name, type, digest);
|
||||
}
|
||||
|
||||
int dim_measure_static_baseline_search(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return static_baseline_search(m, name, type, digest);
|
||||
}
|
||||
|
||||
int dim_measure_dynamic_baseline_search(struct dim_measure *m,
|
||||
const char *name, int type,
|
||||
struct dim_digest *digest)
|
||||
{
|
||||
if (m == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return dynamic_baseline_search(m, name, type, digest);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
static const char* status_name[MEASURE_STATUS_LAST + 1] = {
|
||||
[MEASURE_STATUS_OFF] = "DIM_OFF",
|
||||
[MEASURE_STATUS_NO_BASELINE] = "DIM_NO_BASELINE",
|
||||
[MEASURE_STATUS_BASELINE_RUNNING] = "DIM_BASELINE_RUNNING",
|
||||
[MEASURE_STATUS_MEASURE_RUNNING] = "DIM_MEASURE_RUNNING",
|
||||
[MEASURE_STATUS_PROTECTED] = "DIM_PROTECTED",
|
||||
[MEASURE_STATUS_ERROR] = "DIM_ERROR",
|
||||
[MEASURE_STATUS_LAST] = "DIM_UNKNOWN",
|
||||
};
|
||||
|
||||
const char *dim_measure_status_print(struct dim_measure *m)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (m == NULL)
|
||||
return status_name[MEASURE_STATUS_LAST];
|
||||
|
||||
status = atomic_read(&m->status);
|
||||
if (status < 0 || status >= MEASURE_STATUS_LAST)
|
||||
status = MEASURE_STATUS_LAST;
|
||||
|
||||
return status_name[status];
|
||||
}
|
||||
|
||||
bool dim_measure_status_error(struct dim_measure *m)
|
||||
{
|
||||
return atomic_read(&m->status) == MEASURE_STATUS_ERROR;
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
static void call_measure_func(int mode, struct dim_measure_task *t,
|
||||
struct dim_measure *m)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (t->measure == NULL) {
|
||||
dim_warn("no measure function in %s task", t->name);
|
||||
return;
|
||||
}
|
||||
|
||||
dim_info("start to call %s measure task\n", t->name);
|
||||
ret = t->measure(mode, m);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to call measure task %s: %d\n", t->name, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
dim_info("succeed to call measure task %s\n", t->name);
|
||||
}
|
||||
|
||||
void dim_measure_task_measure(int mode, struct dim_measure *m)
|
||||
{
|
||||
int ret = 0;
|
||||
int status = 0;
|
||||
struct dim_measure_task *task = NULL;
|
||||
|
||||
if (m == NULL)
|
||||
return;
|
||||
|
||||
mutex_lock(&m->measure_lock);
|
||||
status = atomic_read(&m->status);
|
||||
if (mode == DIM_MEASURE && status != MEASURE_STATUS_PROTECTED) {
|
||||
dim_info("no baseline, do baseline init instead\n");
|
||||
mode = DIM_BASELINE;
|
||||
}
|
||||
|
||||
atomic_set(&m->status, mode == DIM_BASELINE ?
|
||||
MEASURE_STATUS_BASELINE_RUNNING :
|
||||
MEASURE_STATUS_MEASURE_RUNNING);
|
||||
|
||||
if (mode == DIM_BASELINE && m->baseline_prepare != NULL) {
|
||||
ret = m->baseline_prepare(m);
|
||||
if (ret < 0) {
|
||||
atomic_set(&m->status, MEASURE_STATUS_ERROR);
|
||||
mutex_unlock(&m->measure_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(task, &m->task_list, node)
|
||||
call_measure_func(mode, task, m);
|
||||
|
||||
atomic_set(&m->status, MEASURE_STATUS_PROTECTED);
|
||||
mutex_unlock(&m->measure_lock);
|
||||
}
|
||||
|
||||
static int task_register(struct dim_measure *m, struct dim_measure_task *t)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (t == NULL || t->name == NULL || t->measure == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (t->init != NULL) {
|
||||
ret = t->init();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_add_tail(&t->node, &m->task_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void task_unregister(struct dim_measure_task *t)
|
||||
{
|
||||
if (t->destroy != NULL)
|
||||
t->destroy();
|
||||
|
||||
list_del(&t->node);
|
||||
}
|
||||
|
||||
int dim_measure_tasks_register(struct dim_measure *m,
|
||||
struct dim_measure_task **tasks,
|
||||
unsigned int num)
|
||||
{
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
|
||||
if (m == NULL || tasks == NULL || num == 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (; i < num; i++) {
|
||||
ret = task_register(m, tasks[i]);
|
||||
if (ret < 0) {
|
||||
dim_measure_tasks_unregister_all(m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dim_info("register measure task: %s\n", tasks[i]->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dim_measure_tasks_unregister_all(struct dim_measure *m)
|
||||
{
|
||||
struct dim_measure_task *pos = NULL;
|
||||
struct dim_measure_task *n = NULL;
|
||||
|
||||
if (m == NULL)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(pos, n, &m->task_list, node)
|
||||
task_unregister(pos);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
void dim_measure_schedule(struct dim_measure *m)
|
||||
{
|
||||
if (m == NULL || m->schedule_jiffies == 0)
|
||||
return;
|
||||
|
||||
schedule_timeout_uninterruptible(m->schedule_jiffies);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_MONITOR_H
|
||||
#define __DIM_MONITOR_H
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
#define DIM_MONITOR_HASH_DEFAULT "sha256"
|
||||
#define DIM_MONITOR_LOG_CAP_DEFAULT 100000
|
||||
|
||||
#define DIM_CORE "dim_core"
|
||||
#define DIM_CORE_TEXT "dim_core.text"
|
||||
#define DIM_CORE_DATA "dim_core.data"
|
||||
|
||||
extern struct dim_measure dim_monitor_handle;
|
||||
|
||||
void dim_monitor_destroy_fs(void);
|
||||
int dim_monitor_create_fs(void);
|
||||
|
||||
int dim_monitor_measure_init(struct dim_measure_cfg *cfg);
|
||||
void dim_monitor_measure_destroy(void);
|
||||
int dim_monitor_measure_blocking(void);
|
||||
int dim_monitor_baseline_blocking(void);
|
||||
const char *dim_monitor_status_print(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "dim_entry.h"
|
||||
#include "dim_utils.h"
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
#include "dim_monitor.h"
|
||||
|
||||
extern struct dim_entry *dim_root_entry(void);
|
||||
|
||||
/*
|
||||
* monitor trigger interface
|
||||
* dim_entry struct: dim_monitor_measure_entry
|
||||
* file entry name: monitor_run
|
||||
* function: dim_monitor_measure_blocking()
|
||||
*/
|
||||
dim_trigger_entry(dim_monitor_measure, monitor_run, dim_monitor_measure_blocking);
|
||||
|
||||
/*
|
||||
* monitor baseline trigger interface
|
||||
* dim_entry struct: dim_monitor_baseline_entry
|
||||
* file entry name: monitor_baseline
|
||||
* function: dim_monitor_baseline_blocking()
|
||||
*/
|
||||
dim_trigger_entry(dim_monitor_baseline, monitor_baseline, dim_monitor_baseline_blocking);
|
||||
|
||||
/*
|
||||
* status print interface
|
||||
* dim_entry struct: dim_status_entry
|
||||
* file entry name: monitor_status
|
||||
* print function: dim_core_status_print
|
||||
*/
|
||||
dim_string_print_entry(dim_monitor_status, monitor_status, dim_monitor_status_print);
|
||||
|
||||
/*
|
||||
* measure log read interface
|
||||
* dim_entry struct: dim_measure_log_entry
|
||||
* file entry name: runtime_status
|
||||
* status to read: dim_measure_log_tree
|
||||
*/
|
||||
dim_measure_log_entry(dim_monitor_log, monitor_ascii_runtime_measurements,
|
||||
&dim_monitor_handle.log);
|
||||
|
||||
static struct dim_entry *dim_monitor_files[] = {
|
||||
&dim_monitor_measure_entry,
|
||||
&dim_monitor_baseline_entry,
|
||||
&dim_monitor_status_entry,
|
||||
&dim_monitor_log_entry,
|
||||
};
|
||||
|
||||
void dim_monitor_destroy_fs(void)
|
||||
{
|
||||
unsigned int len = DIM_ARRAY_LEN(dim_monitor_files);
|
||||
dim_entry_remove_list(dim_monitor_files, len);
|
||||
}
|
||||
|
||||
int dim_monitor_create_fs(void)
|
||||
{
|
||||
struct dim_entry *dim_root = dim_root_entry();
|
||||
unsigned int len = DIM_ARRAY_LEN(dim_monitor_files);
|
||||
|
||||
if (dim_root == NULL || dim_root->dentry == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
return dim_entry_create_list(dim_monitor_files, len, dim_root->dentry);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
#include "dim_monitor.h"
|
||||
#include "dim_monitor_symbol.h"
|
||||
|
||||
/* common measurement configuration */
|
||||
static struct dim_measure_cfg cfg = {
|
||||
.alg_name = DIM_MONITOR_HASH_DEFAULT,
|
||||
.log_cap = DIM_MONITOR_LOG_CAP_DEFAULT,
|
||||
};
|
||||
|
||||
module_param_named(measure_log_capacity, cfg.log_cap, uint, 0);
|
||||
MODULE_PARM_DESC(measure_log_capacity, "Max number of measure log");
|
||||
|
||||
module_param_named(measure_hash, cfg.alg_name, charp, 0);
|
||||
MODULE_PARM_DESC(measure_hash, "Hash algorithm for measurement");
|
||||
|
||||
module_param_named(measure_pcr, cfg.pcr, uint, 0);
|
||||
MODULE_PARM_DESC(measure_pcr, "TPM PCR index to extend measure log");
|
||||
|
||||
static int __init dim_monitor_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dim_monitor_kallsyms_init();
|
||||
if (ret < 0) {
|
||||
dim_err("fail to initialize dim kernel symbol: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = dim_monitor_measure_init(&cfg);
|
||||
if (ret < 0) {
|
||||
dim_err("fail to initialize dim measurement: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = dim_monitor_create_fs();
|
||||
if (ret < 0) {
|
||||
dim_err("fail to create dim fs entry: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dim_monitor_measure_destroy();
|
||||
dim_monitor_destroy_fs();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dim_monitor_exit(void)
|
||||
{
|
||||
dim_monitor_measure_destroy();
|
||||
dim_monitor_destroy_fs();
|
||||
|
||||
#ifdef DIM_DEBUG_MEMORY_LEAK
|
||||
dim_check_memory_leak();
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(dim_monitor_init);
|
||||
module_exit(dim_monitor_exit);
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/genalloc.h>
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
#include "dim_core_mem_pool.h"
|
||||
|
||||
#include "dim_monitor.h"
|
||||
#include "dim_monitor_symbol.h"
|
||||
|
||||
#include "measure_task/dim_monitor_measure_task.h"
|
||||
|
||||
/* measurement tasks */
|
||||
static struct dim_measure_task *dim_core_tasks[] = {
|
||||
&dim_monitor_measure_data,
|
||||
&dim_monitor_measure_text,
|
||||
};
|
||||
|
||||
/* the global measurement handle */
|
||||
struct dim_measure dim_monitor_handle = {
|
||||
.task_list = LIST_HEAD_INIT(dim_monitor_handle.task_list),
|
||||
};
|
||||
|
||||
/* lock to prevent trigger multiple measurement */
|
||||
DEFINE_MUTEX(dim_monitor_measure_lock);
|
||||
|
||||
const char *dim_monitor_status_print(void)
|
||||
{
|
||||
return dim_measure_status_print(&dim_monitor_handle);
|
||||
}
|
||||
|
||||
int dim_monitor_measure_blocking(void)
|
||||
{
|
||||
if (!mutex_trylock(&dim_monitor_measure_lock))
|
||||
return -EBUSY;
|
||||
|
||||
dim_measure_task_measure(DIM_MEASURE, &dim_monitor_handle);
|
||||
mutex_unlock(&dim_monitor_measure_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dim_monitor_baseline_blocking(void)
|
||||
{
|
||||
if (!mutex_trylock(&dim_monitor_measure_lock))
|
||||
return -EBUSY;
|
||||
|
||||
dim_measure_task_measure(DIM_BASELINE, &dim_monitor_handle);
|
||||
mutex_unlock(&dim_monitor_measure_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int baseline_prepare(struct dim_measure *m)
|
||||
{
|
||||
dim_baseline_destroy_tree(&m->static_baseline);
|
||||
dim_baseline_destroy_tree(&m->dynamic_baseline);
|
||||
dim_measure_log_refresh(&m->log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dim_monitor_measure_init(struct dim_measure_cfg *cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* init the measurement handle */
|
||||
ret = dim_measure_init(&dim_monitor_handle, cfg);
|
||||
if (ret < 0) {
|
||||
dim_err("failed to init measurement handle\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set the baseline prepare function */
|
||||
dim_monitor_handle.baseline_prepare = baseline_prepare;
|
||||
|
||||
/* register all measurement tasks */
|
||||
ret = dim_measure_tasks_register(&dim_monitor_handle, dim_core_tasks,
|
||||
DIM_ARRAY_LEN(dim_core_tasks));
|
||||
if (ret < 0) {
|
||||
dim_err("failed to register measure tasks: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dim_measure_destroy(&dim_monitor_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dim_monitor_measure_destroy(void)
|
||||
{
|
||||
mutex_lock(&dim_monitor_measure_lock);
|
||||
dim_measure_destroy(&dim_monitor_handle);
|
||||
mutex_unlock(&dim_monitor_measure_lock);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
#include "dim_symbol.h"
|
||||
|
||||
#include "dim_monitor.h"
|
||||
#include "dim_monitor_symbol.h"
|
||||
|
||||
struct dim_monitor_kallsyms dim_monitor_kernel_symbol;
|
||||
|
||||
int dim_monitor_kallsyms_init(void)
|
||||
{
|
||||
struct dim_monitor_kallsyms *k = &dim_monitor_kernel_symbol;
|
||||
DIM_SYMBOL_LOOKUP_FUNC dim_kallsyms_lookup_name = NULL;
|
||||
|
||||
dim_kallsyms_lookup_name = dim_get_symbol_lookup_func();
|
||||
if (dim_kallsyms_lookup_name == NULL) {
|
||||
dim_err("fail to get symbol_lookup_func\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(k, 0, sizeof(struct dim_monitor_kallsyms));
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||
k->find_module = (DIM_FIND_MODULE)
|
||||
dim_kallsyms_lookup_name("find_module");
|
||||
return k->find_module == NULL ? -ENOENT : 0;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_MONITOR_SYMBOL_H
|
||||
#define __DIM_MONITOR_SYMBOL_H
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||
typedef struct module *(*DIM_FIND_MODULE)(const char *);
|
||||
#endif
|
||||
|
||||
struct dim_monitor_kallsyms {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||
DIM_FIND_MODULE find_module;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern struct dim_monitor_kallsyms dim_monitor_kernel_symbol;
|
||||
|
||||
int dim_monitor_kallsyms_init(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dim_measure.h"
|
||||
#include "dim_core_mem_pool.h"
|
||||
|
||||
#include "dim_monitor.h"
|
||||
|
||||
#include "dim_monitor_measure_task.h"
|
||||
|
||||
static void calculate_chunk(struct gen_pool *pool,
|
||||
struct gen_pool_chunk *chunk,
|
||||
void *data)
|
||||
{
|
||||
struct shash_desc *shash = (struct shash_desc *)data;
|
||||
|
||||
if (chunk == NULL || shash == NULL)
|
||||
return;
|
||||
|
||||
(void)crypto_shash_update(shash, (char *)chunk->start_addr,
|
||||
chunk->end_addr - chunk->start_addr);
|
||||
}
|
||||
|
||||
static int module_text_measure(int mode, struct dim_measure *m)
|
||||
{
|
||||
int ret = 0;
|
||||
int log_flag = LOG_DYNAMIC_BASELINE;
|
||||
struct dim_digest digest = {
|
||||
.algo = m->hash.algo,
|
||||
};
|
||||
|
||||
SHASH_DESC_ON_STACK(shash, m->hash.tfm);
|
||||
shash->tfm = m->hash.tfm;
|
||||
|
||||
ret = crypto_shash_init(shash);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dim_mem_pool_walk_chunk(calculate_chunk, shash);
|
||||
ret = crypto_shash_final(shash, digest.data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = dim_measure_process_dynamic_result(m, mode, DIM_CORE_DATA,
|
||||
&digest, &log_flag);
|
||||
if (ret < 0)
|
||||
dim_err("failed to check dim_core data digest: %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dim_measure_task dim_monitor_measure_data = {
|
||||
.name = "dim_monitor_measure_data",
|
||||
.init = NULL,
|
||||
.destroy = NULL,
|
||||
.measure = module_text_measure,
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DIM_MONITOR_MEASURE_TASK_H
|
||||
#define __DIM_MONITOR_MEASURE_TASK_H
|
||||
|
||||
extern struct dim_measure_task dim_monitor_measure_data;
|
||||
extern struct dim_measure_task dim_monitor_measure_text;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "dim_measure.h"
|
||||
|
||||
#include "dim_monitor.h"
|
||||
#include "dim_monitor_symbol.h"
|
||||
#include "dim_monitor_measure_task.h"
|
||||
|
||||
static int module_text_measure(int mode, struct dim_measure *m)
|
||||
{
|
||||
int ret = 0;
|
||||
int log_flag = LOG_DYNAMIC_BASELINE;
|
||||
struct module *mod = NULL;
|
||||
struct dim_digest digest = { 0 };
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
mutex_lock(&module_mutex);
|
||||
mod = find_module(DIM_CORE);
|
||||
#else
|
||||
rcu_read_lock_sched();
|
||||
mod = dim_monitor_kernel_symbol.find_module(DIM_CORE);
|
||||
#endif
|
||||
if (mod == NULL || mod->state != MODULE_STATE_LIVE ||
|
||||
!try_module_get(mod))
|
||||
mod = NULL; /* target module not exist or is not alive */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
mutex_unlock(&module_mutex);
|
||||
#else
|
||||
rcu_read_unlock_sched();
|
||||
#endif
|
||||
if (mod == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
ret = dim_hash_calculate(mod->core_layout.base,
|
||||
mod->core_layout.text_size,
|
||||
&m->hash, &digest);
|
||||
#else
|
||||
ret = dim_hash_calculate(mod->mem[MOD_TEXT].base,
|
||||
mod->mem[MOD_TEXT].size,
|
||||
&m->hash, &digest);
|
||||
#endif
|
||||
module_put(mod);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = dim_measure_process_dynamic_result(m, mode, DIM_CORE_TEXT,
|
||||
&digest, &log_flag);
|
||||
if (ret < 0)
|
||||
dim_err("failed to check dim_core text digest: %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dim_measure_task dim_monitor_measure_text = {
|
||||
.name = "dim_monitor_measure_text",
|
||||
.init = NULL,
|
||||
.destroy = NULL,
|
||||
.measure = module_text_measure,
|
||||
};
|
Loading…
Reference in New Issue