mirror of
https://github.com/openharmony/kernel_linux_common_modules.git
synced 2026-08-26 17:16:37 -04:00
+1
-1
@@ -12,7 +12,7 @@ ccflags-$(CONFIG_SECURITY_CODE_SIGN) += \
|
||||
-I$(srctree)/fs/code_sign \
|
||||
-I$(srctree)/security/selinux/include \
|
||||
-I$(srctree)/security/selinux \
|
||||
-I$(srctree)/security/xpm/include
|
||||
-I$(srctree)/security/xpm/developer
|
||||
|
||||
$(addprefix $(obj)/,$(obj-y)): $(obj)/flask.h
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ out:
|
||||
int elf_file_enable_fs_verity(struct file *file)
|
||||
{
|
||||
/* developer mode */
|
||||
if (strcmp(developer_mode_state(), DEVELOPER_STATUS_ON)) {
|
||||
if (get_developer_mode_state() != STATE_ON) {
|
||||
code_sign_log_info("developer mode off, elf not allowed to execute");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
|
||||
#include <linux/code_sign.h>
|
||||
#include <linux/fsverity.h>
|
||||
#include <linux/stringhash.h>
|
||||
|
||||
#include "code_sign_ext.h"
|
||||
#include "code_sign_log.h"
|
||||
|
||||
static time64_t cs_salt;
|
||||
|
||||
/**
|
||||
* Validate code sign descriptor
|
||||
*
|
||||
@@ -82,3 +85,23 @@ void code_sign_after_measurement(void *_desc, int version)
|
||||
desc->version = version;
|
||||
}
|
||||
}
|
||||
|
||||
void code_sign_init_salt(void)
|
||||
{
|
||||
cs_salt = ktime_get_real_seconds();
|
||||
}
|
||||
|
||||
void code_sign_set_ownerid(struct cs_info *cs_info, uint32_t id_type,
|
||||
const char *id_str, uint32_t id_len)
|
||||
{
|
||||
if (!cs_info) {
|
||||
code_sign_log_error("Input cs_info is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
cs_info->id_type = id_type;
|
||||
if (!id_str || id_len == 0)
|
||||
cs_info->ownerid = 0;
|
||||
else
|
||||
cs_info->ownerid = full_name_hash(cs_salt, id_str, id_len);
|
||||
}
|
||||
@@ -6,6 +6,34 @@
|
||||
#ifndef _CODE_SIGN_EXT_H
|
||||
#define _CODE_SIGN_EXT_H
|
||||
|
||||
#include <linux/xpm_types.h>
|
||||
|
||||
#define OWNERID_SYSTEM_TAG "SYSTEM_LIB_ID"
|
||||
#define OWNERID_DEBUG_TAG "DEBUG_LIB_ID"
|
||||
#define OWNERID_SHARED_TAG "SHARED_LIB_ID"
|
||||
#define OWNERID_COMPAT_TAG "COMPAT_LIB_ID"
|
||||
|
||||
enum file_ownerid_type {
|
||||
FILE_OWNERID_UNINT = 0,
|
||||
FILE_OWNERID_SYSTEM,
|
||||
FILE_OWNERID_APP,
|
||||
FILE_OWNERID_DEBUG,
|
||||
FILE_OWNERID_SHARED,
|
||||
FILE_OWNERID_COMPAT,
|
||||
FILE_OWNERID_MAX
|
||||
};
|
||||
|
||||
/* process and file ownerid types need to correspond to each other */
|
||||
enum process_ownerid_type {
|
||||
PROCESS_OWNERID_UNINIT = FILE_OWNERID_UNINT,
|
||||
PROCESS_OWNERID_SYSTEM = FILE_OWNERID_SYSTEM,
|
||||
PROCESS_OWNERID_APP = FILE_OWNERID_APP,
|
||||
PROCESS_OWNERID_DEBUG = FILE_OWNERID_DEBUG,
|
||||
PROCESS_OWNERID_COMPAT = FILE_OWNERID_COMPAT,
|
||||
PROCESS_OWNERID_EXTEND,
|
||||
PROCESS_OWNERID_MAX
|
||||
};
|
||||
|
||||
/*
|
||||
* code_sign_ext.c
|
||||
*/
|
||||
@@ -16,4 +44,9 @@ void code_sign_before_measurement(void *_desc, int *ret);
|
||||
|
||||
void code_sign_after_measurement(void *_desc, int version);
|
||||
|
||||
void code_sign_init_salt(void);
|
||||
|
||||
void code_sign_set_ownerid(struct cs_info *cs_info, uint32_t id_type,
|
||||
const char *id_str, uint32_t id_len);
|
||||
|
||||
#endif /* _CODE_SIGN_H */
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <linux/compat.h>
|
||||
#include "avc.h"
|
||||
#include "objsec.h"
|
||||
#include "../../security/xpm/include/dsmm_developer.h"
|
||||
#include "dsmm_developer.h"
|
||||
#include "code_sign_ioctl.h"
|
||||
#include "code_sign_log.h"
|
||||
|
||||
@@ -231,7 +231,7 @@ int code_sign_check_code(int code)
|
||||
return is_dev_mode;
|
||||
|
||||
// developer mode
|
||||
if (!strcmp(developer_mode_state(), DEVELOPER_STATUS_ON)) {
|
||||
if (get_developer_mode_state() == STATE_ON) {
|
||||
code_sign_log_debug("developer mode on");
|
||||
is_dev_mode = 1;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,10 @@ static void code_sign_register_hck_hooks(void)
|
||||
static int __init code_sign_init(void)
|
||||
{
|
||||
code_sign_log_info("INIT");
|
||||
|
||||
/* init module init real time as salt for ownerid calculate */
|
||||
code_sign_init_salt();
|
||||
|
||||
code_sign_register_hck_hooks();
|
||||
return misc_register(&code_sign_misc);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#include <linux/verification.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include "objsec.h"
|
||||
#include "../../security/xpm/include/dsmm_developer.h"
|
||||
#include "dsmm_developer.h"
|
||||
#include "code_sign_ext.h"
|
||||
#include "code_sign_ioctl.h"
|
||||
#include "code_sign_log.h"
|
||||
#include "verify_cert_chain.h"
|
||||
@@ -62,6 +63,43 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_file_ownerid(struct cs_info *cs_info, int path_type,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
/* Mark a debug file as OWNERID_DEBUG */
|
||||
if((path_type > DEBUG_CODE_START) && (path_type < DEBUG_CODE_END)) {
|
||||
code_sign_set_ownerid(cs_info, FILE_OWNERID_DEBUG, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mark the file as OWNERID_COMPAT, if its ownerid is empty */
|
||||
if(!sinfo->ownerid) {
|
||||
code_sign_set_ownerid(cs_info, FILE_OWNERID_COMPAT, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mark the file as OWNERID_SHARED, if the file is shareable */
|
||||
if((sinfo->ownerid_len == strlen(OWNERID_SHARED_TAG)) &&
|
||||
!memcmp(sinfo->ownerid, OWNERID_SHARED_TAG,
|
||||
sinfo->ownerid_len)) {
|
||||
code_sign_set_ownerid(cs_info, FILE_OWNERID_SHARED, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If this code is signed on the device, check whether it is DEBUG_ID */
|
||||
if((path_type = MAY_LOCAL_CODE) &&
|
||||
(sinfo->ownerid_len == strlen(OWNERID_DEBUG_TAG)) &&
|
||||
!memcmp(sinfo->ownerid, OWNERID_DEBUG_TAG,
|
||||
sinfo->ownerid_len)) {
|
||||
code_sign_set_ownerid(cs_info, FILE_OWNERID_DEBUG, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mark the file OWNERID_APP in other cases */
|
||||
code_sign_set_ownerid(cs_info, FILE_OWNERID_APP,
|
||||
sinfo->ownerid, sinfo->ownerid_len);
|
||||
}
|
||||
|
||||
static struct cert_source *find_matched_source(const struct x509_certificate *signer, bool is_debug)
|
||||
{
|
||||
int block_type = is_debug ? DEBUG_BLOCK_CODE: RELEASE_BLOCK_CODE;
|
||||
@@ -76,7 +114,8 @@ static struct cert_source *find_matched_source(const struct x509_certificate *si
|
||||
return source;
|
||||
}
|
||||
|
||||
void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *ret)
|
||||
void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len,
|
||||
struct cs_info *cs_info, int *ret)
|
||||
{
|
||||
struct pkcs7_message *pkcs7;
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
@@ -103,7 +142,7 @@ void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *re
|
||||
bool is_dev_mode = false;
|
||||
|
||||
// developer mode && developer proc
|
||||
if (!strcmp(developer_mode_state(), DEVELOPER_STATUS_ON)) {
|
||||
if (get_developer_mode_state() == STATE_ON) {
|
||||
code_sign_log_info("developer mode on");
|
||||
is_dev_mode = true;
|
||||
}
|
||||
@@ -157,6 +196,7 @@ void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *re
|
||||
}
|
||||
if (cert_chain_depth_without_root == (source->max_path_depth - 1)) {
|
||||
code_sign_log_info("cert subject and issuer trusted");
|
||||
set_file_ownerid(cs_info, source->path_type, pkcs7->signed_infos);
|
||||
*ret = source->path_type;
|
||||
goto exit;
|
||||
} else {
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
#ifndef _VERIFY_CERT_CHAIN_H
|
||||
#define _VERIFY_CERT_CHAIN_H
|
||||
|
||||
#include <linux/xpm_types.h>
|
||||
|
||||
/*
|
||||
* verify_cert_chain.c
|
||||
*/
|
||||
|
||||
void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *ret);
|
||||
void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len,
|
||||
struct cs_info *cs_info, int *ret);
|
||||
|
||||
#endif /* _VERIFY_CERT_CHAIN_H */
|
||||
|
||||
Executable → Regular
+2
-1
@@ -9,6 +9,7 @@ menu "Executable permission manager"
|
||||
config SECURITY_XPM
|
||||
def_bool $(success, $(srctree)/scripts/ohos-check-dir.sh $(srctree)/security/xpm)
|
||||
depends on 64BIT
|
||||
depends on SECURITY_CODE_SIGN
|
||||
help
|
||||
The Executable Permission Manager(XPM) control process execution
|
||||
by inserting control poliy into the security hook list, such as execv,
|
||||
@@ -22,7 +23,7 @@ config DSMM_DEVELOPER_ENABLE
|
||||
help
|
||||
This option should only be enabled for the device support developer
|
||||
mode feature. But whether or not developer mode is enabled on the
|
||||
device ultimately depends on the developer_mode valude in cmdline.
|
||||
device ultimately depends on the developer_mode string in cmdline.
|
||||
|
||||
config SECURITY_XPM_DEBUG
|
||||
bool "Enables excutable permission manager debug mode"
|
||||
|
||||
Executable → Regular
+10
-5
@@ -6,22 +6,27 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SECURITY_XPM) += \
|
||||
core/xpm_common.o \
|
||||
core/xpm_module.o \
|
||||
core/xpm_misc.o \
|
||||
core/xpm_hck.o \
|
||||
core/xpm_misc_device.o \
|
||||
core/xpm_hck_hooks.o \
|
||||
core/xpm_security_hooks.o \
|
||||
core/xpm_report.o \
|
||||
core/dsmm_developer.o \
|
||||
validator/elf_code_segment_info.o \
|
||||
validator/exec_signature_info.o
|
||||
validator/exec_signature_info.o \
|
||||
developer/dsmm_developer.o
|
||||
|
||||
obj-$(CONFIG_SECURITY_XPM_DEBUG) += \
|
||||
core/xpm_debugfs.o
|
||||
|
||||
ccflags-$(CONFIG_SECURITY_XPM) += \
|
||||
-I$(srctree)/security/xpm/include \
|
||||
-I$(srctree)/security/xpm/core \
|
||||
-I$(srctree)/security/xpm/validator \
|
||||
-I$(srctree)/security/xpm/developer \
|
||||
-I$(srctree)/security/selinux/include \
|
||||
-I$(srctree)/security/selinux \
|
||||
-I$(srctree)/fs \
|
||||
-I$(srctree)/fs/verity \
|
||||
-I$(srctree)/fs/code_sign
|
||||
|
||||
$(addprefix $(obj)/,$(obj-y)): $(obj)/flask.h
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include "xpm_common.h"
|
||||
|
||||
bool xpm_is_anonymous_vma(struct vm_area_struct *vma)
|
||||
{
|
||||
return vma_is_anonymous(vma) || vma_is_shmem(vma);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _XPM_COMMON_H
|
||||
#define _XPM_COMMON_H
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
/**
|
||||
* xpm_is_anonymous_vma - Determine whether vma is anonymous.
|
||||
*
|
||||
* @vma: Pointer to "struct vm_area_struct" which need to be determined.
|
||||
*
|
||||
* Returns true on anonymunt, 0 on permissive mode.
|
||||
*
|
||||
* NOTE: shemem also been treated as anonymous vma.
|
||||
*/
|
||||
bool xpm_is_anonymous_vma(struct vm_area_struct *vma);
|
||||
|
||||
#endif /* _XPM_COMMON_H */
|
||||
Executable → Regular
+5
-7
@@ -4,19 +4,17 @@
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include "xpm_log.h"
|
||||
#include "xpm_hck.h"
|
||||
#include "xpm_debugfs.h"
|
||||
|
||||
#define XPM_PERMISSIVE_MODE 0
|
||||
#define XPM_ENFORCE_MODE 1
|
||||
#include "xpm_debugfs.h"
|
||||
#include "xpm_hck_hooks.h"
|
||||
#include "xpm_log.h"
|
||||
|
||||
static struct dentry *xpm_dir;
|
||||
static uint8_t xpm_mode = XPM_PERMISSIVE_MODE;
|
||||
|
||||
bool xpm_is_permissve_mode(void)
|
||||
int xpm_ret(int ret)
|
||||
{
|
||||
return xpm_mode == XPM_PERMISSIVE_MODE;
|
||||
return xpm_mode == XPM_ENFORCE_MODE ? ret : 0;
|
||||
}
|
||||
|
||||
int xpm_debugfs_init(void)
|
||||
|
||||
Executable → Regular
+13
-3
@@ -5,11 +5,21 @@
|
||||
|
||||
#ifndef _XPM_DEBUGFS_H
|
||||
|
||||
#define XPM_PERMISSIVE_MODE 0
|
||||
#define XPM_ENFORCE_MODE 1
|
||||
|
||||
#ifdef CONFIG_SECURITY_XPM_DEBUG
|
||||
int xpm_debugfs_init(void);
|
||||
void xpm_debugfs_exit(void);
|
||||
bool xpm_is_permissve_mode(void);
|
||||
|
||||
/**
|
||||
* xpm_ret - Return value adapted to xpm enforce and permissive modes.
|
||||
*
|
||||
* @ret: Return value.
|
||||
*
|
||||
* Returns ret on enforce mode, 0 on permissive mode.
|
||||
*/
|
||||
int xpm_ret(int ret);
|
||||
#else
|
||||
static inline int xpm_debugfs_init(void)
|
||||
{
|
||||
@@ -20,9 +30,9 @@ static inline void xpm_debugfs_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool xpm_is_permissve_mode(void)
|
||||
static inline int xpm_ret(int ret)
|
||||
{
|
||||
return true;
|
||||
return XPM_PERMISSIVE_MODE;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,385 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/hck/lite_hck_xpm.h>
|
||||
#include <linux/fsverity.h>
|
||||
#include "avc.h"
|
||||
#include "objsec.h"
|
||||
#include "xpm_hck.h"
|
||||
#include "xpm_log.h"
|
||||
#include "xpm_report.h"
|
||||
#include "xpm_debugfs.h"
|
||||
#include "exec_signature_info.h"
|
||||
|
||||
static int xpm_value(int value)
|
||||
{
|
||||
return xpm_is_permissve_mode() ? 0 : value;
|
||||
}
|
||||
|
||||
static bool xpm_is_anonymous_vma(struct vm_area_struct *vma)
|
||||
{
|
||||
return vma_is_anonymous(vma) || vma_is_shmem(vma);
|
||||
}
|
||||
|
||||
static int xpm_avc_has_perm(u16 tclass, u32 requested)
|
||||
{
|
||||
struct av_decision avd;
|
||||
u32 sid = current_sid();
|
||||
int rc, rc2;
|
||||
|
||||
rc = avc_has_perm_noaudit(&selinux_state, sid, sid, tclass, requested,
|
||||
AVC_STRICT, &avd);
|
||||
rc2 = avc_audit(&selinux_state, sid, sid, tclass, requested, &avd, rc,
|
||||
NULL, AVC_STRICT);
|
||||
if (rc2)
|
||||
return rc2;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int xpm_validate_signature(struct vm_area_struct *vma,
|
||||
struct exec_file_signature_info *info)
|
||||
{
|
||||
unsigned long verified_data_end, vm_addr_end;
|
||||
const struct inode *inode = (const struct inode *)info->inode;
|
||||
|
||||
if (IS_ERR_OR_NULL(info))
|
||||
return xpm_avc_has_perm(SECCLASS_XPM, XPM__EXEC_NO_SIGN);
|
||||
|
||||
if(!exec_file_signature_is_fs_verity(info))
|
||||
return 0;
|
||||
|
||||
vm_addr_end = (vma->vm_pgoff << PAGE_SHIFT)
|
||||
+ (vma->vm_end - vma->vm_start);
|
||||
verified_data_end = PAGE_ALIGN(fsverity_get_verified_data_size(inode));
|
||||
if (verified_data_end < vm_addr_end) {
|
||||
xpm_log_error("data is out of verified data size.");
|
||||
return -EPERM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xpm_check_code_segment(bool is_exec, struct vm_area_struct *vma,
|
||||
struct exec_file_signature_info *info)
|
||||
{
|
||||
int i;
|
||||
unsigned long vm_addr_start, vm_addr_end;
|
||||
unsigned long seg_addr_start, seg_addr_end;
|
||||
struct exec_segment_info *segments = info->code_segments;
|
||||
|
||||
if (!is_exec)
|
||||
return 0;
|
||||
|
||||
if (!segments) {
|
||||
xpm_log_error("code segments is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vm_addr_start = vma->vm_pgoff << PAGE_SHIFT;
|
||||
vm_addr_end = vm_addr_start + (vma->vm_end - vma->vm_start);
|
||||
|
||||
for (i = 0; i < info->code_segment_count; i++) {
|
||||
seg_addr_start = ALIGN_DOWN(segments[i].file_offset, PAGE_SIZE);
|
||||
seg_addr_end = PAGE_ALIGN(segments[i].file_offset +
|
||||
segments[i].size);
|
||||
if ((vm_addr_start >= seg_addr_start) &&
|
||||
(vm_addr_end <= seg_addr_end))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return xpm_avc_has_perm(SECCLASS_XPM, XPM__EXEC_NO_SIGN);
|
||||
}
|
||||
|
||||
static void xpm_check_signature_error(struct file *file, int err_num)
|
||||
{
|
||||
char *full_path;
|
||||
char *path;
|
||||
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
path = __getname();
|
||||
if (path == NULL) {
|
||||
xpm_log_error("malloc file name failed");
|
||||
return;
|
||||
}
|
||||
|
||||
full_path = file_path(file, path, PATH_MAX - 1);
|
||||
if (IS_ERR(full_path)) {
|
||||
xpm_log_error("get file d_path failed");
|
||||
return;
|
||||
}
|
||||
|
||||
xpm_log_error("xpm get %s signature info failed, errno = %d", full_path, -err_num);
|
||||
__putname(path);
|
||||
return;
|
||||
}
|
||||
|
||||
static int xpm_check_signature(struct vm_area_struct *vma, unsigned long prot)
|
||||
{
|
||||
int ret;
|
||||
bool is_exec;
|
||||
struct exec_file_signature_info *info = NULL;
|
||||
|
||||
/* vma is non-executable or mmap in xpm region just return */
|
||||
is_exec = !xpm_is_anonymous_vma(vma) && (prot & PROT_EXEC);
|
||||
if (!((vma->vm_flags & VM_XPM) || is_exec))
|
||||
return 0;
|
||||
|
||||
/* validate signature when vma is mmap in xpm region or executable */
|
||||
ret = get_exec_file_signature_info(vma->vm_file, is_exec, &info);
|
||||
if (ret) {
|
||||
xpm_check_signature_error(vma->vm_file, ret);
|
||||
report_file_event(TYPE_FORMAT_UNDEF, vma->vm_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xpm_validate_signature(vma, info);
|
||||
if (ret) {
|
||||
xpm_log_error("xpm validate signature info failed");
|
||||
report_mmap_event(TYPE_SIGN_INVALID, vma, is_exec, prot);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = xpm_check_code_segment(is_exec, vma, info);
|
||||
if (ret) {
|
||||
xpm_log_error("xpm check executable vma mmap code segment failed");
|
||||
report_mmap_event(TYPE_DATA_MMAP_CODE, vma, is_exec, prot);
|
||||
goto exit;
|
||||
}
|
||||
exit:
|
||||
put_exec_file_signature_info(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xpm_check_prot(struct vm_area_struct *vma, unsigned long prot)
|
||||
{
|
||||
int ret;
|
||||
bool is_anon;
|
||||
|
||||
is_anon = xpm_is_anonymous_vma(vma);
|
||||
if ((vma->vm_flags & VM_XPM) && (is_anon || (prot & PROT_WRITE) ||
|
||||
(prot & PROT_EXEC))) {
|
||||
xpm_log_error("xpm region mmap not allow anonymous/exec/write permission");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* anonymous executable permission need controled by selinux */
|
||||
if (is_anon && (prot & PROT_EXEC)) {
|
||||
ret = xpm_avc_has_perm(SECCLASS_XPM, XPM__EXEC_ANON_MEM);
|
||||
if (ret) {
|
||||
xpm_log_error("anonymous mmap not allow exec permission");
|
||||
report_mmap_event(TYPE_ANON_EXEC, vma, TYPE_ANON, prot);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_anon && (prot & PROT_WRITE) && (prot & PROT_EXEC)) {
|
||||
xpm_log_error("file mmap not allow write & exec permission");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xpm_common_check(struct vm_area_struct *vma, unsigned long prot)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = xpm_check_prot(vma, prot);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = xpm_check_signature(vma, prot);
|
||||
} while (0);
|
||||
|
||||
return xpm_value(ret);
|
||||
}
|
||||
|
||||
static int xpm_mmap_check(struct vm_area_struct *vma)
|
||||
{
|
||||
return xpm_common_check(vma, vma->vm_flags);
|
||||
}
|
||||
|
||||
static int xpm_mprotect_check(struct vm_area_struct *vma,
|
||||
unsigned long reqprot, unsigned long prot)
|
||||
{
|
||||
(void)reqprot;
|
||||
|
||||
return xpm_common_check(vma, prot);
|
||||
}
|
||||
|
||||
void xpm_delete_cache_node(struct inode *file_node)
|
||||
{
|
||||
delete_exec_file_signature_info(file_node);
|
||||
}
|
||||
|
||||
static void xpm_region_outer(unsigned long addr_start, unsigned long addr_end,
|
||||
unsigned long flags, bool *ret)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
if (!mm)
|
||||
return;
|
||||
|
||||
/* Already in xpm region, just return without judge */
|
||||
if (flags & VM_UNMAPPED_AREA_XPM)
|
||||
return;
|
||||
|
||||
*ret = ((addr_start >= mm->xpm_region.addr_end) ||
|
||||
(addr_end <= mm->xpm_region.addr_start));
|
||||
}
|
||||
|
||||
void xpm_get_unmapped_area(unsigned long addr, unsigned long len,
|
||||
unsigned long map_flags, unsigned long unmapped_flags,
|
||||
unsigned long *ret)
|
||||
{
|
||||
struct vm_unmapped_area_info info;
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
if (!mm)
|
||||
return;
|
||||
|
||||
if ((mm->xpm_region.addr_start == 0) && (mm->xpm_region.addr_end == 0))
|
||||
return;
|
||||
|
||||
if ((map_flags & MAP_FIXED) && !(addr >= mm->xpm_region.addr_end ||
|
||||
addr + len <= mm->xpm_region.addr_start)) {
|
||||
xpm_log_error("xpm region not allow mmap with MAP_FIXED");
|
||||
*ret = -EFAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (map_flags & MAP_XPM) {
|
||||
if (addr) {
|
||||
xpm_log_error("xpm region not allow specify addr");
|
||||
*ret = -EPERM;
|
||||
return;
|
||||
}
|
||||
|
||||
info.flags = VM_UNMAPPED_AREA_XPM | unmapped_flags;
|
||||
info.length = len;
|
||||
info.low_limit = mm->xpm_region.addr_start;
|
||||
info.high_limit = mm->xpm_region.addr_end;
|
||||
info.align_mask = 0;
|
||||
info.align_offset = 0;
|
||||
|
||||
*ret = vm_unmapped_area(&info);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A xpm readonly region is an area where any page mapped
|
||||
* will be marked with XPMReadonly.
|
||||
* Return 1 if a region is readonly, otherwise, return 0.
|
||||
*/
|
||||
static bool is_xpm_readonly_region(struct vm_area_struct *vma)
|
||||
{
|
||||
/* 1. xpm region */
|
||||
if (vma->vm_flags & VM_XPM)
|
||||
return true;
|
||||
|
||||
/* 2. !anonymous && executable */
|
||||
if (!xpm_is_anonymous_vma(vma) && (vma->vm_flags & VM_EXEC))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void xpm_integrity_check(struct vm_area_struct *vma, unsigned int vflags,
|
||||
unsigned long addr, struct page *page, vm_fault_t *ret)
|
||||
{
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
/* integrity violation: write a readonly page */
|
||||
if ((vflags & FAULT_FLAG_WRITE) && (vma->vm_flags & VM_WRITE) &&
|
||||
PageXPMReadonly(page)) {
|
||||
report_integrity_event(TYPE_INTEGRITY_RO, vma, page);
|
||||
*ret = xpm_value(VM_FAULT_SIGSEGV);
|
||||
return;
|
||||
}
|
||||
|
||||
/* integrity violation: execute a writetained page */
|
||||
if (PageXPMWritetainted(page) && is_xpm_readonly_region(vma)) {
|
||||
report_integrity_event(TYPE_INTEGRITY_WT, vma, page);
|
||||
*ret = xpm_value(VM_FAULT_SIGSEGV);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void xpm_integrity_update(struct vm_area_struct *vma, unsigned int vflags,
|
||||
struct page *page)
|
||||
{
|
||||
/* set writetainted only if a real write occurred */
|
||||
if ((vflags & FAULT_FLAG_WRITE) && (vma->vm_flags & VM_WRITE) &&
|
||||
!PageXPMWritetainted(page)) {
|
||||
SetPageXPMWritetainted(page);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set xpm readonly flag */
|
||||
if (is_xpm_readonly_region(vma) && !PageXPMReadonly(page))
|
||||
SetPageXPMReadonly(page);
|
||||
}
|
||||
|
||||
void xpm_integrity_validate(struct vm_area_struct *vma, unsigned int vflags,
|
||||
unsigned long addr, struct page *page, vm_fault_t *ret)
|
||||
{
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
xpm_integrity_check(vma, vflags, addr, page, ret);
|
||||
if (!*ret)
|
||||
xpm_integrity_update(vma, vflags, page);
|
||||
}
|
||||
|
||||
/*
|
||||
* check the integrity of these two pages, return true if equal,
|
||||
* otherwise false
|
||||
*/
|
||||
void xpm_integrity_equal(struct page *page, struct page *kpage, bool *ret)
|
||||
{
|
||||
if (!page || !kpage)
|
||||
return;
|
||||
|
||||
*ret = ((PageXPMWritetainted(page) == PageXPMWritetainted(kpage)) &&
|
||||
(PageXPMReadonly(page) == PageXPMReadonly(kpage)));
|
||||
}
|
||||
|
||||
static struct security_hook_list xpm_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(mmap_region, xpm_mmap_check),
|
||||
LSM_HOOK_INIT(file_mprotect, xpm_mprotect_check),
|
||||
};
|
||||
|
||||
void xpm_register_xpm_hooks(void)
|
||||
{
|
||||
security_add_hooks(xpm_hooks, ARRAY_SIZE(xpm_hooks), "xpm");
|
||||
}
|
||||
|
||||
void xpm_register_hck_hooks(void)
|
||||
{
|
||||
REGISTER_HCK_LITE_HOOK(xpm_delete_cache_node_lhck,
|
||||
xpm_delete_cache_node);
|
||||
|
||||
REGISTER_HCK_LITE_HOOK(xpm_region_outer_lhck, xpm_region_outer);
|
||||
REGISTER_HCK_LITE_HOOK(xpm_get_unmapped_area_lhck,
|
||||
xpm_get_unmapped_area);
|
||||
|
||||
/* xpm integrity */
|
||||
REGISTER_HCK_LITE_HOOK(xpm_integrity_equal_lhck, xpm_integrity_equal);
|
||||
REGISTER_HCK_LITE_HOOK(xpm_integrity_check_lhck, xpm_integrity_check);
|
||||
REGISTER_HCK_LITE_HOOK(xpm_integrity_update_lhck, xpm_integrity_update);
|
||||
REGISTER_HCK_LITE_HOOK(xpm_integrity_validate_lhck,
|
||||
xpm_integrity_validate);
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <asm/page.h>
|
||||
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/hck/lite_hck_xpm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/mm.h>
|
||||
|
||||
#include "exec_signature_info.h"
|
||||
#include "xpm_common.h"
|
||||
#include "xpm_debugfs.h"
|
||||
#include "xpm_hck_hooks.h"
|
||||
#include "xpm_log.h"
|
||||
#include "xpm_report.h"
|
||||
|
||||
static void xpm_delete_cache_node(struct inode *file_node)
|
||||
{
|
||||
delete_exec_file_signature_info(file_node);
|
||||
}
|
||||
|
||||
static void xpm_region_outer(unsigned long addr_start, unsigned long addr_end,
|
||||
unsigned long flags, bool *ret)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
if (!mm)
|
||||
return;
|
||||
|
||||
/*
|
||||
* VM_UNMAPPED_AREA_XPM identifies the address to allocated in the
|
||||
* xpm_region, just ignore.
|
||||
*/
|
||||
if (flags & VM_UNMAPPED_AREA_XPM)
|
||||
return;
|
||||
|
||||
*ret = ((addr_start >= mm->xpm_region.addr_end) ||
|
||||
(addr_end <= mm->xpm_region.addr_start));
|
||||
}
|
||||
|
||||
void xpm_get_unmapped_area(unsigned long addr, unsigned long len,
|
||||
unsigned long map_flags, unsigned long unmapped_flags,
|
||||
unsigned long *ret)
|
||||
{
|
||||
struct vm_unmapped_area_info info;
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
if (!mm)
|
||||
return;
|
||||
|
||||
if ((mm->xpm_region.addr_start == 0) && (mm->xpm_region.addr_end == 0))
|
||||
return;
|
||||
|
||||
if ((map_flags & MAP_FIXED) && !(addr >= mm->xpm_region.addr_end ||
|
||||
addr + len <= mm->xpm_region.addr_start)) {
|
||||
xpm_log_error("xpm region not allow mmap with MAP_FIXED");
|
||||
*ret = -EFAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (map_flags & MAP_XPM) {
|
||||
if (addr) {
|
||||
xpm_log_error("xpm region not allow specify addr");
|
||||
*ret = -EPERM;
|
||||
return;
|
||||
}
|
||||
|
||||
info.flags = VM_UNMAPPED_AREA_XPM | unmapped_flags;
|
||||
info.length = len;
|
||||
info.low_limit = mm->xpm_region.addr_start;
|
||||
info.high_limit = mm->xpm_region.addr_end;
|
||||
info.align_mask = 0;
|
||||
info.align_offset = 0;
|
||||
|
||||
*ret = vm_unmapped_area(&info);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A xpm readonly region is an area where any page mapped
|
||||
* will be marked with XPMReadonly.
|
||||
*
|
||||
* Return 1 if a region is readonly, otherwise, return 0.
|
||||
*/
|
||||
static bool is_xpm_readonly_region(struct vm_area_struct *vma)
|
||||
{
|
||||
/* xpm region */
|
||||
if (vma->vm_flags & VM_XPM)
|
||||
return true;
|
||||
|
||||
/* !anonymous && executable */
|
||||
if (!xpm_is_anonymous_vma(vma) && (vma->vm_flags & VM_EXEC))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void xpm_integrity_check(struct vm_area_struct *vma, unsigned int vflags,
|
||||
unsigned long addr, struct page *page, vm_fault_t *ret)
|
||||
{
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
/* integrity violation: write a readonly page */
|
||||
if ((vflags & FAULT_FLAG_WRITE) && (vma->vm_flags & VM_WRITE) &&
|
||||
PageXPMReadonly(page)) {
|
||||
report_integrity_event(INTEGRITY_RO, vma, page);
|
||||
*ret = xpm_ret(VM_FAULT_SIGSEGV);
|
||||
return;
|
||||
}
|
||||
|
||||
/* integrity violation: execute a writetained page */
|
||||
if (PageXPMWritetainted(page) && is_xpm_readonly_region(vma)) {
|
||||
report_integrity_event(INTEGRITY_WT, vma, page);
|
||||
*ret = xpm_ret(VM_FAULT_SIGSEGV);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void xpm_integrity_update(struct vm_area_struct *vma, unsigned int vflags,
|
||||
struct page *page)
|
||||
{
|
||||
/* set writetainted only if a real write occurred */
|
||||
if ((vflags & FAULT_FLAG_WRITE) && (vma->vm_flags & VM_WRITE) &&
|
||||
!PageXPMWritetainted(page)) {
|
||||
SetPageXPMWritetainted(page);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set xpm readonly flag */
|
||||
if (is_xpm_readonly_region(vma) && !PageXPMReadonly(page))
|
||||
SetPageXPMReadonly(page);
|
||||
}
|
||||
|
||||
void xpm_integrity_validate(struct vm_area_struct *vma, unsigned int vflags,
|
||||
unsigned long addr, struct page *page, vm_fault_t *ret)
|
||||
{
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
xpm_integrity_check(vma, vflags, addr, page, ret);
|
||||
if (!*ret)
|
||||
xpm_integrity_update(vma, vflags, page);
|
||||
}
|
||||
|
||||
/*
|
||||
* check the integrity of these two pages.
|
||||
*
|
||||
* Return true if equal, otherwise false.
|
||||
*/
|
||||
void xpm_integrity_equal(struct page *page, struct page *kpage, bool *ret)
|
||||
{
|
||||
if (!page || !kpage)
|
||||
return;
|
||||
|
||||
*ret = ((PageXPMWritetainted(page) == PageXPMWritetainted(kpage)) &&
|
||||
(PageXPMReadonly(page) == PageXPMReadonly(kpage)));
|
||||
}
|
||||
|
||||
void xpm_register_hck_hooks(void)
|
||||
{
|
||||
REGISTER_HCK_LITE_HOOK(xpm_delete_cache_node_lhck,
|
||||
xpm_delete_cache_node);
|
||||
|
||||
REGISTER_HCK_LITE_HOOK(xpm_region_outer_lhck, xpm_region_outer);
|
||||
REGISTER_HCK_LITE_HOOK(xpm_get_unmapped_area_lhck,
|
||||
xpm_get_unmapped_area);
|
||||
|
||||
/* xpm integrity */
|
||||
REGISTER_HCK_LITE_HOOK(xpm_integrity_equal_lhck, xpm_integrity_equal);
|
||||
REGISTER_HCK_LITE_HOOK(xpm_integrity_check_lhck, xpm_integrity_check);
|
||||
REGISTER_HCK_LITE_HOOK(xpm_integrity_update_lhck, xpm_integrity_update);
|
||||
REGISTER_HCK_LITE_HOOK(xpm_integrity_validate_lhck,
|
||||
xpm_integrity_validate);
|
||||
}
|
||||
@@ -3,13 +3,9 @@
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _XPM_HCK_H
|
||||
#define _XPM_HCK_H
|
||||
|
||||
void set_xpm_mode(uint8_t mode);
|
||||
|
||||
void xpm_register_xpm_hooks(void);
|
||||
#ifndef _XPM_HCK_HOOKS_H
|
||||
#define _XPM_HCK_HOOKS_H
|
||||
|
||||
void xpm_register_hck_hooks(void);
|
||||
|
||||
#endif /* _XPM_HCK_H */
|
||||
#endif /* _XPM_HCK_HOOKS_H */
|
||||
@@ -6,8 +6,6 @@
|
||||
#ifndef _XPM_LOG_H
|
||||
#define _XPM_LOG_H
|
||||
|
||||
#define XPM_CHECK_FAILED (-1024)
|
||||
|
||||
#define XPM_TAG "xpm_kernel"
|
||||
#define XPM_INFO_TAG "I"
|
||||
#define XPM_ERROR_TAG "E"
|
||||
Executable → Regular
+65
-34
@@ -3,64 +3,103 @@
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include "xpm_misc.h"
|
||||
|
||||
#include <linux/code_sign.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/xpm_types.h>
|
||||
|
||||
#include "code_sign_ext.h"
|
||||
#include "xpm_log.h"
|
||||
#include "xpm_misc_device.h"
|
||||
#include "xpm_report.h"
|
||||
|
||||
#define XPM_SET_REGION _IOW('x', 0x01, struct xpm_region_info)
|
||||
#define XPM_SET_REGION _IOW('x', 0x01, struct xpm_config)
|
||||
#define XPM_SET_OWNERID _IOW('x', 0x02, struct xpm_config)
|
||||
|
||||
static int xpm_set_region(unsigned long addr_base, unsigned long length)
|
||||
static int xpm_set_region(struct xpm_config *config)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long addr;
|
||||
uint64_t addr;
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
if (!mm)
|
||||
return -EINVAL;
|
||||
|
||||
if (mmap_write_lock_killable(mm))
|
||||
return -EINTR;
|
||||
|
||||
if ((mm->xpm_region.addr_start != 0) ||
|
||||
(mm->xpm_region.addr_end != 0)) {
|
||||
xpm_log_info("xpm region has been set");
|
||||
goto exit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr = get_unmapped_area(NULL, addr_base, length, 0, 0);
|
||||
if (IS_ERR_VALUE(addr) || (ULONG_MAX - addr_base < length)) {
|
||||
addr = get_unmapped_area(NULL, config->region_addr,
|
||||
config->region_length, 0, 0);
|
||||
if (IS_ERR_VALUE(addr) || (ULLONG_MAX - addr < config->region_length)) {
|
||||
xpm_log_error("xpm get unmmaped area failed");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mmap_write_lock_killable(mm))
|
||||
return -EINTR;
|
||||
|
||||
mm->xpm_region.addr_start = addr;
|
||||
mm->xpm_region.addr_end = addr + length;
|
||||
exit:
|
||||
mm->xpm_region.addr_end = addr + config->region_length;
|
||||
mmap_write_unlock(mm);
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xpm_set_ownerid(struct xpm_config *config)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
if (!mm)
|
||||
return -EINVAL;
|
||||
|
||||
if (config->id_type >= PROCESS_OWNERID_MAX) {
|
||||
xpm_log_error("input ownerid type is invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SECURITY_XPM_DEBUG
|
||||
if ((mm->pcs_info.id_type == PROCESS_OWNERID_APP) ||
|
||||
mm->pcs_info.id_type == PROCESS_OWNERID_DEBUG) {
|
||||
xpm_log_info("process ownerid has been set");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (config->ownerid[MAX_OWNERID_LEN - 1] != '\0') {
|
||||
xpm_log_error("input ownerid string is invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mmap_write_lock_killable(mm))
|
||||
return -EINTR;
|
||||
|
||||
code_sign_set_ownerid(&mm->pcs_info, config->id_type,
|
||||
config->ownerid, strlen(config->ownerid));
|
||||
mmap_write_unlock(mm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long xpm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret;
|
||||
struct xpm_region_info info = {0};
|
||||
struct xpm_config config = {0};
|
||||
|
||||
if (unlikely(copy_from_user(&info, (void __user *)(uintptr_t)arg,
|
||||
sizeof(struct xpm_region_info))))
|
||||
if (unlikely(copy_from_user(&config, u64_to_user_ptr((uint64_t)arg),
|
||||
sizeof(struct xpm_config))))
|
||||
return -EFAULT;
|
||||
|
||||
switch (cmd) {
|
||||
case XPM_SET_REGION:
|
||||
ret = xpm_set_region(info.addr_base, info.length);
|
||||
ret = xpm_set_region(&config);
|
||||
break;
|
||||
case XPM_SET_OWNERID:
|
||||
ret = xpm_set_ownerid(&config);
|
||||
break;
|
||||
default:
|
||||
xpm_log_error("xpm ioctl cmd error, cmd = %d", cmd);
|
||||
@@ -71,14 +110,6 @@ static long xpm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long xpm_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int xpm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
@@ -95,7 +126,7 @@ static const struct file_operations xpm_fops = {
|
||||
.release = xpm_release,
|
||||
.unlocked_ioctl = xpm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = xpm_compat_ioctl,
|
||||
.compat_ioctl = xpm_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _XPM_MISC_DEVICE_H
|
||||
#define _XPM_MISC_DEVICE_H
|
||||
|
||||
#include <linux/xpm_types.h>
|
||||
|
||||
#define MAX_OWNERID_LEN 64
|
||||
|
||||
struct xpm_config {
|
||||
uint64_t region_addr;
|
||||
uint64_t region_length;
|
||||
|
||||
uint32_t id_type;
|
||||
char ownerid[MAX_OWNERID_LEN];
|
||||
};
|
||||
|
||||
int xpm_register_misc_device(void);
|
||||
void xpm_deregister_misc_device(void);
|
||||
|
||||
#endif /* _XPM_MISC_DEVICE_H */
|
||||
Executable → Regular
+10
-8
@@ -7,12 +7,14 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include "xpm_log.h"
|
||||
#include "xpm_hck.h"
|
||||
#include "xpm_misc.h"
|
||||
#include "xpm_report.h"
|
||||
#include "xpm_debugfs.h"
|
||||
|
||||
#include "dsmm_developer.h"
|
||||
#include "xpm_debugfs.h"
|
||||
#include "xpm_hck_hooks.h"
|
||||
#include "xpm_misc_device.h"
|
||||
#include "xpm_security_hooks.h"
|
||||
#include "xpm_report.h"
|
||||
#include "xpm_log.h"
|
||||
|
||||
static int __init xpm_module_init(void)
|
||||
{
|
||||
@@ -21,7 +23,7 @@ static int __init xpm_module_init(void)
|
||||
ret = xpm_register_misc_device();
|
||||
if (ret) {
|
||||
xpm_log_error("xpm register misc device failed, ret = %d", ret);
|
||||
report_init_event(TYPE_DEVICEFS_UNINIT);
|
||||
report_init_event(DEVICEFS_UNINIT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -29,11 +31,11 @@ static int __init xpm_module_init(void)
|
||||
if (ret) {
|
||||
xpm_log_error("xpm init debugfs failed, ret = %d", ret);
|
||||
xpm_deregister_misc_device();
|
||||
report_init_event(TYPE_DEBUGFS_UNINIT);
|
||||
report_init_event(DEBUGFS_UNINIT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
xpm_register_xpm_hooks();
|
||||
xpm_register_security_hooks();
|
||||
xpm_register_hck_hooks();
|
||||
|
||||
dsmm_developer_proc_create();
|
||||
|
||||
+184
-148
@@ -3,18 +3,30 @@
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/rmap.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xpm.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fsverity.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/rmap.h>
|
||||
#include <linux/xpm.h>
|
||||
|
||||
#ifdef CONFIG_HW_KERNEL_SG
|
||||
#include <security/security_guard_collect.h>
|
||||
#endif
|
||||
|
||||
#include "code_sign_ext.h"
|
||||
#include "fsverity_private.h"
|
||||
#include "xpm_log.h"
|
||||
#include "xpm_report.h"
|
||||
|
||||
#define PROT_MASK (PROT_EXEC | PROT_READ | PROT_WRITE)
|
||||
static char *code_type_tbl[] = {
|
||||
[TYPE_ABC] = "ABC",
|
||||
[TYPE_ELF] = "ELF",
|
||||
[TYPE_ANON] = "ANON"
|
||||
};
|
||||
|
||||
#ifndef CONFIG_HW_KERNEL_SG
|
||||
typedef struct {
|
||||
unsigned long event_id;
|
||||
@@ -22,43 +34,51 @@ typedef struct {
|
||||
unsigned int content_len;
|
||||
char content[0];
|
||||
} event_info;
|
||||
|
||||
unsigned int report_security_info(const event_info *event)
|
||||
{
|
||||
xpm_log_info("%d: %s", event->event_id, event->content);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *xpm_get_filename(struct xpm_event_param *param, char *buf, int len)
|
||||
unsigned int xpm_report_security_info(const event_info *event)
|
||||
{
|
||||
char *filename = NULL;
|
||||
struct file *file = NULL;
|
||||
xpm_log_error("%d: %s", event->event_id, event->content);
|
||||
|
||||
if (param->file)
|
||||
file = param->file;
|
||||
else if (param->vma && param->vma->vm_file)
|
||||
file = param->vma->vm_file;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
filename = d_absolute_path(&file->f_path, buf, len);
|
||||
if (IS_ERR(filename)) {
|
||||
xpm_log_error("xpm get absolute path failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return filename;
|
||||
#ifdef CONFIG_HW_KERNEL_SG
|
||||
return report_security_info(event);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int set_init_content(struct xpm_event_param *param,
|
||||
static void xpm_set_filename(struct file *file, struct xpm_report_info *info)
|
||||
{
|
||||
char *filename = NULL;
|
||||
char *buffer = NULL;
|
||||
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
buffer = kzalloc(sizeof(info->filename), GFP_ATOMIC);
|
||||
if (!buffer) {
|
||||
xpm_log_error("alloc filename buffer failed");
|
||||
return;
|
||||
}
|
||||
|
||||
filename = file_path(file, buffer, sizeof(info->filename) - 1);
|
||||
if (IS_ERR(filename)) {
|
||||
xpm_log_error("xpm set file path failed");
|
||||
} else {
|
||||
strcpy(info->filename, filename);
|
||||
}
|
||||
|
||||
kfree(buffer);
|
||||
}
|
||||
|
||||
static int set_init_content(struct xpm_report_info *info,
|
||||
uint8_t *content, uint32_t content_len)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = snprintf(content, content_len,
|
||||
"{ "JSTR_PAIR(event_type, %s)", "JVAL_PAIR(timestamp, %llu)" }",
|
||||
param->event_type, param->timestamp);
|
||||
info->event_type, info->timestamp);
|
||||
|
||||
if (len < 0 || len > content_len) {
|
||||
xpm_log_error("snprintf init content failed");
|
||||
@@ -68,32 +88,24 @@ static int set_init_content(struct xpm_event_param *param,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PROT_MASK (PROT_EXEC | PROT_READ | PROT_WRITE)
|
||||
const static char *code_type[] = {
|
||||
[TYPE_ABC] = "ABC",
|
||||
[TYPE_ELF] = "ELF",
|
||||
[TYPE_ANON] = "ANON"
|
||||
};
|
||||
static int set_mmap_content(struct xpm_event_param *param, uint8_t *content,
|
||||
static int set_mmap_content(struct xpm_report_info *info, uint8_t *content,
|
||||
uint32_t content_len)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (!param->vma) {
|
||||
xpm_log_error("input vma is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len = snprintf(content, content_len,
|
||||
"{ "JSTR_PAIR(event_type, %s)", "JVAL_PAIR(timestamp, %llu)", "
|
||||
JVAL_PAIR(pid, %u)", "JSTR_PAIR(filename, %s)", "
|
||||
JSTR_PAIR(code_type, %s)", "JVAL_PAIR(prot, %lu)","
|
||||
JVAL_PAIR(pgoff, %lu)", "JVAL_PAIR(size, %lu)" }",
|
||||
param->event_type, param->timestamp, param->pid,
|
||||
param->filename ? param->filename : "",
|
||||
code_type[param->code], param->prot & PROT_MASK,
|
||||
param->vma->vm_pgoff,
|
||||
param->vma->vm_end - param->vma->vm_start);
|
||||
"{ "JSTR_PAIR(event_type, %s)", "JSTR_PAIR(code_type, %s)", "
|
||||
JVAL_PAIR(pid, %u)", "JSTR_PAIR(comm, %s)", "
|
||||
JSTR_PAIR(filename, %s)", "JVAL_PAIR(vm_prot, %lu)","
|
||||
JVAL_PAIR(vm_pgoff, %lu)", "JVAL_PAIR(vm_size, %lu)", "
|
||||
JVAL_PAIR(p_id_type, %u)", "JSTR_PAIR(p_ownerid, %u)", "
|
||||
JVAL_PAIR(f_id_type, %u)", "JSTR_PAIR(f_ownerid, %u)", "
|
||||
JSTR_PAIR(timestamp, %llu)" }",
|
||||
info->event_type, info->code_type, info->pid, info->comm,
|
||||
info->filename, info->vm_prot, info->vm_pgoff, info->vm_size,
|
||||
info->pcs_info.id_type, info->pcs_info.ownerid,
|
||||
info->fcs_info.id_type, info->fcs_info.ownerid,
|
||||
info->timestamp);
|
||||
|
||||
if (len < 0 || len > content_len) {
|
||||
xpm_log_error("snprintf code mmap content failed");
|
||||
@@ -103,16 +115,17 @@ static int set_mmap_content(struct xpm_event_param *param, uint8_t *content,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_file_content(struct xpm_event_param *param,
|
||||
static int set_file_content(struct xpm_report_info *info,
|
||||
uint8_t *content, uint32_t content_len)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = snprintf(content, content_len,
|
||||
"{ "JSTR_PAIR(event_type, %s)", "JVAL_PAIR(timestamp, %llu)", "
|
||||
JVAL_PAIR(pid, %u)", "JSTR_PAIR(filename, %s)" }",
|
||||
param->event_type, param->timestamp, param->pid,
|
||||
param->filename ? param->filename : "");
|
||||
"{ "JSTR_PAIR(event_type, %s)", "JVAL_PAIR(pid, %u)", "
|
||||
JSTR_PAIR(comm, %s)", "JSTR_PAIR(filename, %s)", "
|
||||
JVAL_PAIR(timestap, %llu)" }",
|
||||
info->event_type, info->pid, info->comm,
|
||||
info->filename, info->timestamp);
|
||||
|
||||
if (len < 0 || len > content_len) {
|
||||
xpm_log_error("snprintf file format content failed");
|
||||
@@ -122,28 +135,19 @@ static int set_file_content(struct xpm_event_param *param,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_integrity_content(struct xpm_event_param *param,
|
||||
static int set_integrity_content(struct xpm_report_info *info,
|
||||
uint8_t *content, uint32_t content_len)
|
||||
{
|
||||
int len;
|
||||
char *page_type;
|
||||
|
||||
if (!param->vma || !param->page) {
|
||||
xpm_log_error("input vma or page is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
page_type = PageKsm(param->page) ?
|
||||
"[ksm]" : PageAnon(param->page) ? "[anon]" : "[file]";
|
||||
|
||||
len = snprintf(content, content_len,
|
||||
"{ " JSTR_PAIR(event_type, %s)", "JVAL_PAIR(timestamp, %llu)", "
|
||||
JVAL_PAIR(pid, %u)","JSTR_PAIR(page_type, %s)", "
|
||||
JSTR_PAIR(filename, %s)", "JVAL_PAIR(page_index, %lu)","
|
||||
JVAL_PAIR(page_prot, %lu)" }",
|
||||
param->event_type, param->timestamp, param->pid, page_type,
|
||||
param->filename ? param->filename : "", param->page->index,
|
||||
param->vma->vm_page_prot.pgprot & PROT_MASK);
|
||||
"{ "JSTR_PAIR(event_type, %s)", "JVAL_PAIR(pid, %u)", "
|
||||
JSTR_PAIR(comm, %s)", "JSTR_PAIR(filename, %s)", "
|
||||
JSTR_PAIR(page_type, %s)", "JVAL_PAIR(page_index, %lu)", "
|
||||
JVAL_PAIR(vm_pgprot, %lu)", "JVAL_PAIR(timestamp, %llu)" }",
|
||||
info->event_type, info->pid, info->comm, info->filename,
|
||||
info->page_type, info->page_index, info->vm_pgprot,
|
||||
info->timestamp);
|
||||
|
||||
if (len < 0 || len > content_len) {
|
||||
xpm_log_error("snprintf init integrity failed");
|
||||
@@ -153,120 +157,152 @@ static int set_integrity_content(struct xpm_event_param *param,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct xpm_event_info xpm_event[] = {
|
||||
[TYPE_DEVICEFS_UNINIT] = { "devicefs uninitialized",
|
||||
EVENT_INIT, set_init_content },
|
||||
[TYPE_DEBUGFS_UNINIT] = { "debugfs uninitialized",
|
||||
EVENT_INIT, set_init_content },
|
||||
[TYPE_DM_DISABLE] = { "dm-verity disable",
|
||||
EVENT_INIT, set_init_content },
|
||||
[TYPE_FORMAT_UNDEF] = { "unkown file format",
|
||||
EVENT_FILE, set_file_content },
|
||||
[TYPE_ANON_EXEC] = { "anon executed",
|
||||
EVENT_MMAP, set_file_content },
|
||||
[TYPE_SIGN_INVALID] = { "invalid signature",
|
||||
EVENT_MMAP, set_mmap_content },
|
||||
[TYPE_DATA_MMAP_CODE] = { "data mmap code",
|
||||
EVENT_MMAP, set_mmap_content },
|
||||
[TYPE_INTEGRITY_RO] = { "code tampered",
|
||||
EVENT_INTEGRITY, set_integrity_content },
|
||||
[TYPE_INTEGRITY_WT] = { "data executed",
|
||||
EVENT_INTEGRITY, set_integrity_content },
|
||||
};
|
||||
|
||||
static int report_event_inner(enum xpm_event_type type,
|
||||
struct xpm_event_param *param, event_info *event)
|
||||
static void xpm_set_report_info(struct xpm_report_param *param,
|
||||
struct xpm_report_info *info)
|
||||
{
|
||||
int ret;
|
||||
struct fsverity_info *vi = NULL;
|
||||
struct file *file = param->file;
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
ret = xpm_event[type].set_content(param, event->content,
|
||||
MAX_CONTENT_LEN);
|
||||
if (ret) {
|
||||
xpm_log_error("type [%d] set content failed", type);
|
||||
return ret;
|
||||
}
|
||||
event->content_len = strlen(event->content);
|
||||
event->event_id = xpm_event[type].event_id;
|
||||
event->version = XPM_EVENT_VERSION;
|
||||
info->event_type = param->event_type;
|
||||
info->code_type = code_type_tbl[param->code_type];
|
||||
|
||||
ret = report_security_info(event);
|
||||
if (ret) {
|
||||
xpm_log_error("type [%d] report security info failed", type);
|
||||
return ret;
|
||||
info->pid = current->pid;
|
||||
memcpy(info->comm, current->comm, TASK_COMM_LEN);
|
||||
|
||||
if (mm) {
|
||||
info->pcs_info.id_type = mm->pcs_info.id_type;
|
||||
info->pcs_info.ownerid = mm->pcs_info.ownerid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
info->vm_prot = param->vm_prot & PROT_MASK;
|
||||
if (param->vma) {
|
||||
info->vm_pgoff = param->vma->vm_pgoff;
|
||||
info->vm_size = param->vma->vm_end - param->vma->vm_start;
|
||||
info->vm_pgprot = param->vma->vm_page_prot.pgprot & PROT_MASK;
|
||||
file = param->vma->vm_file;
|
||||
}
|
||||
|
||||
if (param->page) {
|
||||
info->page_type = PageKsm(param->page) ?
|
||||
"[ksm]" : PageAnon(param->page) ? "[anon]" : "[file]";
|
||||
info->page_index = param->page->index;
|
||||
}
|
||||
|
||||
/* init file ownerid type SYSTEM */
|
||||
info->fcs_info.id_type = FILE_OWNERID_SYSTEM;
|
||||
if (file) {
|
||||
xpm_set_filename(file, info);
|
||||
vi = fsverity_get_info(file_inode(file));
|
||||
if (vi) {
|
||||
info->fcs_info.id_type = vi->fcs_info.id_type;
|
||||
info->fcs_info.ownerid = vi->fcs_info.ownerid;
|
||||
}
|
||||
}
|
||||
|
||||
info->timestamp = ktime_get_real_seconds();
|
||||
}
|
||||
|
||||
static int xpm_report_event(enum xpm_event_type type,
|
||||
struct xpm_event_param *param)
|
||||
static int xpm_report_event(struct xpm_report_param *param)
|
||||
{
|
||||
int ret;
|
||||
event_info *sg_event;
|
||||
char *buf;
|
||||
event_info *event = NULL;
|
||||
struct xpm_report_info *info = NULL;
|
||||
|
||||
if (!(xpm_event[type].set_content)) {
|
||||
xpm_log_error("type [%d] set content func invalid", type);
|
||||
if (!param->event_type) {
|
||||
xpm_log_error("xpm event type is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sg_event = kzalloc(sizeof(event_info) + MAX_CONTENT_LEN, GFP_KERNEL);
|
||||
if (!sg_event) {
|
||||
info = kzalloc(sizeof(struct xpm_report_info), GFP_ATOMIC);
|
||||
if (!info) {
|
||||
xpm_log_error("alloc xpm report info struct failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
event = kzalloc(sizeof(event_info) + MAX_CONTENT_LEN, GFP_ATOMIC);
|
||||
if (!event) {
|
||||
xpm_log_error("alloc security guard event failed");
|
||||
kfree(info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
buf = __getname();
|
||||
if (!buf) {
|
||||
xpm_log_error("alloc file name buf failed");
|
||||
kfree(sg_event);
|
||||
return -ENOMEM;
|
||||
}
|
||||
do {
|
||||
event->version = XPM_EVENT_VERSION;
|
||||
event->event_id = param->event_id;
|
||||
|
||||
param->event_type = xpm_event[type].event_type;
|
||||
param->filename = xpm_get_filename(param, buf, PATH_MAX);
|
||||
param->timestamp = ktime_get_real_seconds();
|
||||
param->pid = current->pid;
|
||||
/* set xpm report info from param */
|
||||
xpm_set_report_info(param, info);
|
||||
ret = param->set_content(info, event->content, MAX_CONTENT_LEN);
|
||||
if (ret) {
|
||||
xpm_log_error("type [%s] set content failed",
|
||||
param->event_type);
|
||||
break;
|
||||
}
|
||||
event->content_len = strlen(event->content);
|
||||
|
||||
ret = report_event_inner(type, param, sg_event);
|
||||
ret = xpm_report_security_info(event);
|
||||
if (ret) {
|
||||
xpm_log_error("type [%s] report security info failed",
|
||||
param->event_type);
|
||||
break;
|
||||
}
|
||||
|
||||
__putname(buf);
|
||||
kfree(sg_event);
|
||||
} while (0);
|
||||
|
||||
kfree(info);
|
||||
kfree(event);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void report_init_event(enum xpm_event_type type)
|
||||
void report_init_event(char *event_type)
|
||||
{
|
||||
struct xpm_event_param param = {0};
|
||||
struct xpm_report_param param = {0};
|
||||
|
||||
xpm_report_ratelimited(xpm_report_event, type, ¶m);
|
||||
param.event_type = event_type;
|
||||
param.event_id = EVENT_INIT;
|
||||
param.set_content = &set_init_content;
|
||||
|
||||
xpm_report_ratelimited(xpm_report_event, ¶m);
|
||||
}
|
||||
|
||||
void report_file_event(enum xpm_event_type type, struct file *file)
|
||||
void report_file_event(char *event_type, struct file *file)
|
||||
{
|
||||
struct xpm_event_param param = {0};
|
||||
struct xpm_report_param param = {0};
|
||||
|
||||
param.event_type = event_type;
|
||||
param.event_id = EVENT_FILE;
|
||||
param.file = file;
|
||||
xpm_report_ratelimited(xpm_report_event, type, ¶m);
|
||||
param.set_content = &set_file_content;
|
||||
|
||||
xpm_report_ratelimited(xpm_report_event, ¶m);
|
||||
}
|
||||
|
||||
void report_mmap_event(enum xpm_event_type type, struct vm_area_struct *vma,
|
||||
int code, int prot)
|
||||
void report_mmap_event(char *event_type, enum xpm_code_type code_type,
|
||||
struct vm_area_struct *vma, unsigned long vm_prot)
|
||||
{
|
||||
struct xpm_event_param param = {0};
|
||||
struct xpm_report_param param = {0};
|
||||
|
||||
param.event_type = event_type;
|
||||
param.event_id = EVENT_MMAP;
|
||||
param.code_type = code_type;
|
||||
param.vma = vma;
|
||||
param.code = code;
|
||||
param.prot = prot;
|
||||
xpm_report_ratelimited(xpm_report_event, type, ¶m);
|
||||
param.vm_prot = vm_prot;
|
||||
param.set_content = &set_mmap_content;
|
||||
|
||||
xpm_report_ratelimited(xpm_report_event, ¶m);
|
||||
}
|
||||
|
||||
void report_integrity_event(enum xpm_event_type type,
|
||||
struct vm_area_struct *vma, struct page *page)
|
||||
void report_integrity_event(char *event_type, struct vm_area_struct *vma,
|
||||
struct page *page)
|
||||
{
|
||||
struct xpm_event_param param = {0};
|
||||
struct xpm_report_param param = {0};
|
||||
|
||||
param.event_type = event_type;
|
||||
param.event_id = EVENT_INTEGRITY;
|
||||
param.vma = vma;
|
||||
param.page = page;
|
||||
xpm_report_ratelimited(xpm_report_event, type, ¶m);
|
||||
param.set_content = &set_integrity_content;
|
||||
|
||||
xpm_report_ratelimited(xpm_report_event, ¶m);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _XPM_REPORT_H
|
||||
#define _XPM_REPORT_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/xpm_types.h>
|
||||
|
||||
#define NULL_STR "NULL"
|
||||
|
||||
#define MAX_FILENAME_LEN 128
|
||||
|
||||
/* EVENT_INIT */
|
||||
#define DEVICEFS_UNINIT "devicefs uninitialized"
|
||||
#define DEBUGFS_UNINIT "debugfs uninitialized"
|
||||
#define DM_DISABLE "dm-verity disable"
|
||||
|
||||
/* EVENT_FILE */
|
||||
#define FORMAT_UNDEF "unkown file format"
|
||||
|
||||
/* EVENT_MMAP */
|
||||
#define ANON_EXEC "anon executed"
|
||||
#define GET_SIGN_FAIL "get signature info failed"
|
||||
#define SIGN_INVALID "invalid signature"
|
||||
#define DATA_MMAP_CODE "data mmap code"
|
||||
#define OWNERID_INCONSISTENT "ownerid inconsistent"
|
||||
|
||||
/* EVENT_INTEGRITY */
|
||||
#define INTEGRITY_RO "code tampered"
|
||||
#define INTEGRITY_WT "data executed"
|
||||
|
||||
enum xpm_code_type {
|
||||
TYPE_ABC = 0,
|
||||
TYPE_ELF,
|
||||
TYPE_ANON,
|
||||
};
|
||||
|
||||
enum xpm_event_id {
|
||||
EVENT_INIT = 1011009110,
|
||||
EVENT_FILE = 1011009111,
|
||||
EVENT_MMAP = 1011009112,
|
||||
EVENT_INTEGRITY = 1011009113,
|
||||
};
|
||||
|
||||
/* set of report info */
|
||||
struct xpm_report_info {
|
||||
char *event_type;
|
||||
char *code_type;
|
||||
|
||||
pid_t pid;
|
||||
char comm[TASK_COMM_LEN];
|
||||
char filename[MAX_FILENAME_LEN + 1];
|
||||
struct cs_info pcs_info;
|
||||
struct cs_info fcs_info;
|
||||
|
||||
unsigned long vm_prot;
|
||||
unsigned long vm_pgprot;
|
||||
unsigned long vm_pgoff;
|
||||
unsigned long vm_size;
|
||||
|
||||
char *page_type;
|
||||
pgoff_t page_index;
|
||||
|
||||
ktime_t timestamp;
|
||||
};
|
||||
|
||||
/* set of caller parameters */
|
||||
struct xpm_report_param {
|
||||
char *event_type;
|
||||
enum xpm_event_id event_id;
|
||||
enum xpm_code_type code_type;
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long vm_prot;
|
||||
struct page *page;
|
||||
struct file *file;
|
||||
|
||||
int (*set_content)(struct xpm_report_info *info, uint8_t *content,
|
||||
uint32_t content_len);
|
||||
};
|
||||
|
||||
#define MAX_CONTENT_LEN 900
|
||||
#define XPM_EVENT_VERSION 0
|
||||
|
||||
#ifndef CONFIG_SECURITY_XPM_DEBUG
|
||||
#define xpm_report_ratelimited(func, fmt, ...) \
|
||||
do { \
|
||||
static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL, \
|
||||
DEFAULT_RATELIMIT_BURST); \
|
||||
if (__ratelimit(&_rs)) \
|
||||
func(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define xpm_report_ratelimited(func, fmt, ...) \
|
||||
func(fmt, ##__VA_ARGS__);
|
||||
|
||||
#endif
|
||||
|
||||
#define JSTR(val) "\""#val"\""
|
||||
#define JVAL_PAIR(val, format) JSTR(val) ": " #format
|
||||
#define JSTR_PAIR(val, format) JSTR(val) ": " JSTR(format)
|
||||
|
||||
void report_init_event(char *event_type);
|
||||
void report_file_event(char *event_type, struct file *file);
|
||||
void report_mmap_event(char *event_type, enum xpm_code_type code_type,
|
||||
struct vm_area_struct *vma, unsigned long prot);
|
||||
void report_integrity_event(char *event_type, struct vm_area_struct *vma,
|
||||
struct page *page);
|
||||
|
||||
#endif /* _XPM_REPORT_H */
|
||||
@@ -0,0 +1,373 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/mman.h>
|
||||
#include <linux/mm_types.h>
|
||||
|
||||
#include "avc.h"
|
||||
#include "objsec.h"
|
||||
#include "exec_signature_info.h"
|
||||
#include "fsverity_private.h"
|
||||
#include "code_sign_ext.h"
|
||||
#include "xpm_common.h"
|
||||
#include "xpm_debugfs.h"
|
||||
#include "xpm_log.h"
|
||||
#include "xpm_report.h"
|
||||
#include "xpm_security_hooks.h"
|
||||
|
||||
enum ownerid_policy_type {
|
||||
DENY = 0,
|
||||
ALLOW,
|
||||
CHECK,
|
||||
};
|
||||
|
||||
static uint32_t ownerid_policy[PROCESS_OWNERID_MAX][FILE_OWNERID_MAX] __ro_after_init;
|
||||
|
||||
static void init_ownerid_policy(void)
|
||||
{
|
||||
ownerid_policy[PROCESS_OWNERID_SYSTEM][FILE_OWNERID_SYSTEM] = ALLOW;
|
||||
|
||||
ownerid_policy[PROCESS_OWNERID_APP][FILE_OWNERID_SYSTEM] = ALLOW;
|
||||
ownerid_policy[PROCESS_OWNERID_APP][FILE_OWNERID_SHARED] = ALLOW;
|
||||
ownerid_policy[PROCESS_OWNERID_APP][FILE_OWNERID_APP] = CHECK;
|
||||
|
||||
ownerid_policy[PROCESS_OWNERID_DEBUG][FILE_OWNERID_SYSTEM] = ALLOW;
|
||||
ownerid_policy[PROCESS_OWNERID_DEBUG][FILE_OWNERID_SHARED] = ALLOW;
|
||||
ownerid_policy[PROCESS_OWNERID_DEBUG][FILE_OWNERID_DEBUG] = ALLOW;
|
||||
|
||||
ownerid_policy[PROCESS_OWNERID_COMPAT][FILE_OWNERID_SYSTEM] = ALLOW;
|
||||
ownerid_policy[PROCESS_OWNERID_COMPAT][FILE_OWNERID_COMPAT] = ALLOW;
|
||||
|
||||
for (int i = 0; i < FILE_OWNERID_MAX; i++) {
|
||||
ownerid_policy[PROCESS_OWNERID_EXTEND][i] = ALLOW;
|
||||
}
|
||||
}
|
||||
|
||||
static int check_same_ownerid(struct cs_info *pcs_info, struct cs_info *fcs_info)
|
||||
{
|
||||
if ((pcs_info->id_type == fcs_info->id_type) &&
|
||||
(pcs_info->ownerid == fcs_info->ownerid)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
int xpm_check_ownerid_policy(struct cs_info *pcs_info, struct cs_info *fcs_info)
|
||||
{
|
||||
uint32_t type;
|
||||
|
||||
if (!pcs_info || !fcs_info) {
|
||||
xpm_log_error("input pcs_info or fcs_info is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((pcs_info->id_type >= PROCESS_OWNERID_MAX) ||
|
||||
(fcs_info->id_type >= FILE_OWNERID_MAX)) {
|
||||
xpm_log_info("process or file ownerid exceed maximum value");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
type = ownerid_policy[pcs_info->id_type][fcs_info->id_type];
|
||||
switch (type) {
|
||||
case DENY:
|
||||
return -EPERM;
|
||||
case ALLOW:
|
||||
return 0;
|
||||
case CHECK:
|
||||
return check_same_ownerid(pcs_info, fcs_info);
|
||||
default:
|
||||
xpm_log_error("input ownerid type is invalid: %u", type);
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int xpm_get_file_cs_info(struct cs_info *fcs_info,
|
||||
struct exec_file_signature_info *info)
|
||||
{
|
||||
/* exec file is dm-verity */
|
||||
if (exec_file_signature_is_dm_verity(info)) {
|
||||
code_sign_set_ownerid(fcs_info, FILE_OWNERID_SYSTEM, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* exec file is fs-verity */
|
||||
if (exec_file_signature_is_fs_verity(info)) {
|
||||
struct fsverity_info *vi = fsverity_get_info(info->inode);
|
||||
if (!vi) {
|
||||
xpm_log_error("get verity info failed in fs-verity");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fcs_info->id_type = vi->fcs_info.id_type;
|
||||
fcs_info->ownerid = vi->fcs_info.ownerid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
xpm_log_error("invalid code signature info type");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int xpm_get_process_cs_info(struct cs_info *pcs_info)
|
||||
{
|
||||
int ret;
|
||||
struct exec_file_signature_info *info = NULL;
|
||||
struct file *exe_file = NULL;
|
||||
struct cs_info fcs_info = {0};
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
if (!mm)
|
||||
return -EINVAL;
|
||||
|
||||
/* process cs_info has not been init, just init from exe file */
|
||||
if (mm->pcs_info.id_type == PROCESS_OWNERID_UNINIT) {
|
||||
exe_file = get_task_exe_file(current);
|
||||
if (!exe_file) {
|
||||
xpm_log_error("xpm get exe_file failed");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
ret = get_exec_file_signature_info(exe_file, true, &info);
|
||||
/* reduce exe_file reference count */
|
||||
fput(exe_file);
|
||||
if (ret || (info == NULL)) {
|
||||
xpm_log_error("xpm get exe_file signature info failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xpm_get_file_cs_info(&fcs_info, info);
|
||||
if (ret) {
|
||||
xpm_log_error("xpm get exe_file cs info failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* process's ownerid is correspond to file */
|
||||
mm->pcs_info.id_type = fcs_info.id_type;
|
||||
mm->pcs_info.ownerid = fcs_info.ownerid;
|
||||
}
|
||||
pcs_info->id_type = mm->pcs_info.id_type;
|
||||
pcs_info->ownerid = mm->pcs_info.ownerid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xpm_check_ownerid(struct vm_area_struct *vma,
|
||||
struct exec_file_signature_info *info)
|
||||
{
|
||||
int ret;
|
||||
struct cs_info pcs_info = {0};
|
||||
struct cs_info fcs_info = {0};
|
||||
|
||||
ret = xpm_get_process_cs_info(&pcs_info);
|
||||
if (ret) {
|
||||
xpm_log_error("xpm get process cs_info falied");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xpm_get_file_cs_info(&fcs_info, info);
|
||||
if (ret) {
|
||||
xpm_log_error("xpm get file cs_info falied");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return xpm_check_ownerid_policy(&pcs_info, &fcs_info);
|
||||
}
|
||||
|
||||
static int xpm_avc_has_perm(u16 tclass, u32 requested)
|
||||
{
|
||||
struct av_decision avd;
|
||||
u32 sid = current_sid();
|
||||
|
||||
return avc_has_perm_noaudit(&selinux_state, sid, sid, tclass, requested,
|
||||
AVC_STRICT, &avd);
|
||||
}
|
||||
|
||||
static int xpm_validate_signature(struct vm_area_struct *vma,
|
||||
struct exec_file_signature_info *info)
|
||||
{
|
||||
unsigned long verified_data_end, vm_addr_end;
|
||||
const struct inode *inode = (const struct inode *)info->inode;
|
||||
|
||||
if (IS_ERR_OR_NULL(info)) {
|
||||
xpm_log_error("signature info is NULL");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if(!exec_file_signature_is_fs_verity(info))
|
||||
return 0;
|
||||
|
||||
vm_addr_end = (vma->vm_pgoff << PAGE_SHIFT)
|
||||
+ (vma->vm_end - vma->vm_start);
|
||||
verified_data_end = PAGE_ALIGN(fsverity_get_verified_data_size(inode));
|
||||
if (verified_data_end < vm_addr_end) {
|
||||
xpm_log_error("data is out of verified data size");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xpm_check_code_segment(bool is_exec, struct vm_area_struct *vma,
|
||||
struct exec_file_signature_info *info)
|
||||
{
|
||||
int i;
|
||||
unsigned long vm_addr_start, vm_addr_end;
|
||||
unsigned long seg_addr_start, seg_addr_end;
|
||||
struct exec_segment_info *segments = info->code_segments;
|
||||
|
||||
if (!is_exec)
|
||||
return 0;
|
||||
|
||||
if (!segments) {
|
||||
xpm_log_error("code segments is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vm_addr_start = vma->vm_pgoff << PAGE_SHIFT;
|
||||
vm_addr_end = vm_addr_start + (vma->vm_end - vma->vm_start);
|
||||
|
||||
for (i = 0; i < info->code_segment_count; i++) {
|
||||
seg_addr_start = ALIGN_DOWN(segments[i].file_offset, PAGE_SIZE);
|
||||
seg_addr_end = PAGE_ALIGN(segments[i].file_offset +
|
||||
segments[i].size);
|
||||
if ((vm_addr_start >= seg_addr_start) &&
|
||||
(vm_addr_end <= seg_addr_end))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static int xpm_check_signature(struct vm_area_struct *vma, unsigned long prot)
|
||||
{
|
||||
int ret;
|
||||
bool is_exec;
|
||||
struct exec_file_signature_info *info = NULL;
|
||||
|
||||
/* vma is non-executable or mmap in xpm region just return */
|
||||
is_exec = !xpm_is_anonymous_vma(vma) && (prot & PROT_EXEC);
|
||||
if (!((vma->vm_flags & VM_XPM) || is_exec))
|
||||
return 0;
|
||||
|
||||
/* process has exec_no_sign permission just return */
|
||||
if (xpm_avc_has_perm(SECCLASS_XPM, XPM__EXEC_NO_SIGN) == 0)
|
||||
return 0;
|
||||
|
||||
/* validate signature when vma is mmap in xpm region or executable */
|
||||
ret = get_exec_file_signature_info(vma->vm_file, is_exec, &info);
|
||||
if (ret) {
|
||||
report_mmap_event(GET_SIGN_FAIL, is_exec ? TYPE_ELF : TYPE_ABC,
|
||||
vma, prot);
|
||||
return ret;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = xpm_validate_signature(vma, info);
|
||||
if (ret) {
|
||||
report_mmap_event(SIGN_INVALID,
|
||||
is_exec ? TYPE_ELF : TYPE_ABC, vma, prot);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = xpm_check_code_segment(is_exec, vma, info);
|
||||
if (ret) {
|
||||
report_mmap_event(DATA_MMAP_CODE,
|
||||
is_exec ? TYPE_ELF : TYPE_ABC, vma, prot);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = xpm_check_ownerid(vma, info);
|
||||
if (ret) {
|
||||
report_mmap_event(OWNERID_INCONSISTENT,
|
||||
is_exec ? TYPE_ELF : TYPE_ABC, vma, prot);
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (info)
|
||||
put_exec_file_signature_info(info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xpm_check_prot(struct vm_area_struct *vma, unsigned long prot)
|
||||
{
|
||||
int ret;
|
||||
bool is_anon;
|
||||
|
||||
is_anon = xpm_is_anonymous_vma(vma);
|
||||
|
||||
/* check for xpm region vma prot */
|
||||
if (vma->vm_flags & VM_XPM) {
|
||||
if (is_anon || (prot & PROT_EXEC)) {
|
||||
xpm_log_error("xpm region mmap not allow anonymous or exec permission");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check for anonymous vma prot, anonymous executable permission need
|
||||
* controled by selinux
|
||||
*/
|
||||
if (is_anon && (prot & PROT_EXEC)) {
|
||||
ret = xpm_avc_has_perm(SECCLASS_XPM, XPM__EXEC_ANON_MEM);
|
||||
if (ret) {
|
||||
report_mmap_event(ANON_EXEC, TYPE_ANON, vma, prot);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check for non-anonymous vma prot */
|
||||
if (!is_anon && (prot & PROT_WRITE) && (prot & PROT_EXEC)) {
|
||||
xpm_log_error("file mmap not allow write & exec permission");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xpm_common_check(struct vm_area_struct *vma, unsigned long prot)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = xpm_check_prot(vma, prot);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = xpm_check_signature(vma, prot);
|
||||
} while (0);
|
||||
|
||||
return xpm_ret(ret);
|
||||
}
|
||||
|
||||
static int xpm_mmap_check(struct vm_area_struct *vma)
|
||||
{
|
||||
return xpm_common_check(vma, vma->vm_flags);
|
||||
}
|
||||
|
||||
static int xpm_mprotect_check(struct vm_area_struct *vma,
|
||||
unsigned long reqprot, unsigned long prot)
|
||||
{
|
||||
(void)reqprot;
|
||||
|
||||
return xpm_common_check(vma, prot);
|
||||
}
|
||||
|
||||
static struct security_hook_list xpm_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(mmap_region, xpm_mmap_check),
|
||||
LSM_HOOK_INIT(file_mprotect, xpm_mprotect_check),
|
||||
};
|
||||
|
||||
void xpm_register_security_hooks(void)
|
||||
{
|
||||
init_ownerid_policy();
|
||||
security_add_hooks(xpm_hooks, ARRAY_SIZE(xpm_hooks), "xpm");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _XPM_SECURITY_HOOKS_H
|
||||
#define _XPM_SECURITY_HOOKS_H
|
||||
|
||||
void xpm_register_security_hooks(void);
|
||||
|
||||
#endif /* _XPM_SECURITY_HOOKS_H */
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#include "dsmm_developer.h"
|
||||
#include "xpm_log.h"
|
||||
|
||||
@@ -12,20 +13,21 @@
|
||||
#define DSMM_DEVELOPER_PARAM_NAME "const.security.developermode.state"
|
||||
|
||||
static struct proc_dir_entry *g_dsmm_dir;
|
||||
static uint32_t developer_state = STATE_UNINT;
|
||||
|
||||
static const char *g_developer_status[BUILD_VARIANT_MAX][DEVELOPER_PROC_STATUS_MAX] = {
|
||||
{ DEVELOPER_STATUS_OFF, DEVELOPER_STATUS_ON, DEVELOPER_STATUS_OFF },
|
||||
{ DEVELOPER_STATUS_ON, DEVELOPER_STATUS_ON, DEVELOPER_STATUS_OFF },
|
||||
static uint32_t g_state_table[BUILD_VARIANT_MAX][CMDLINE_DEV_STATE_MAX] = {
|
||||
{ STATE_OFF, STATE_ON, STATE_OFF },
|
||||
{ STATE_ON, STATE_ON, STATE_ON },
|
||||
};
|
||||
|
||||
static int get_developer_status(uint32_t *status)
|
||||
{
|
||||
if (!strstr(saved_command_line, "developer_mode=")) {
|
||||
*status = DEVELOPER_PROC_STATUS_NA;
|
||||
*status = CMDLINE_DEV_STATE_NA;
|
||||
} else if (strstr(saved_command_line, "developer_mode=1")) {
|
||||
*status = DEVELOPER_PROC_STATUS_ON;
|
||||
*status = CMDLINE_DEV_STATE_ON;
|
||||
} else if (strstr(saved_command_line, "developer_mode=0")) {
|
||||
*status = DEVELOPER_PROC_STATUS_OFF;
|
||||
*status = CMDLINE_DEV_STATE_OFF;
|
||||
} else {
|
||||
xpm_log_error("invalid developer_mode value in cmdline");
|
||||
return -EINVAL;
|
||||
@@ -48,20 +50,25 @@ static int get_build_variant(uint32_t *variant)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *developer_mode_state(void)
|
||||
int get_developer_mode_state(void)
|
||||
{
|
||||
uint32_t variant, status;
|
||||
|
||||
if (developer_state != STATE_UNINT)
|
||||
return developer_state;
|
||||
|
||||
#ifdef CONFIG_DSMM_DEVELOPER_ENABLE
|
||||
if (get_build_variant(&variant) || get_developer_status(&status)) {
|
||||
xpm_log_error("get build variant or developer status failed");
|
||||
return NULL;
|
||||
developer_state = STATE_OFF;
|
||||
} else {
|
||||
developer_state = g_state_table[variant][status];
|
||||
}
|
||||
|
||||
return g_developer_status[variant][status];
|
||||
#else
|
||||
return DEVELOPER_STATUS_ON;
|
||||
developer_state = STATE_ON;
|
||||
#endif
|
||||
|
||||
return developer_state;
|
||||
}
|
||||
|
||||
#define PROC_DEVELOPER_LEN 50
|
||||
@@ -69,16 +76,13 @@ static ssize_t dsmm_read_developer_proc(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
size_t len;
|
||||
uint32_t state;
|
||||
char proc_developer[PROC_DEVELOPER_LEN] = {0};
|
||||
const char *developer_state = developer_mode_state();
|
||||
|
||||
if (!developer_state) {
|
||||
xpm_log_error("developer mode state invalid");
|
||||
return 0;
|
||||
}
|
||||
|
||||
state = get_developer_mode_state();
|
||||
len = snprintf(proc_developer, PROC_DEVELOPER_LEN - 1,
|
||||
DSMM_DEVELOPER_PARAM_NAME"=%s", developer_state);
|
||||
DSMM_DEVELOPER_PARAM_NAME"=%s",
|
||||
state == STATE_ON ? "true" : "false");
|
||||
|
||||
return simple_read_from_buffer(buf, count, pos, proc_developer, len);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _DSMM_DEVELOPER_H
|
||||
#define _DSMM_DEVELOPER_H
|
||||
|
||||
#define STATE_UNINT 0
|
||||
#define STATE_ON 1
|
||||
#define STATE_OFF 2
|
||||
|
||||
enum build_variant {
|
||||
BUILD_VARIANT_USER = 0,
|
||||
BUILD_VARIANT_ENG,
|
||||
|
||||
BUILD_VARIANT_MAX,
|
||||
};
|
||||
|
||||
enum cmdline_dev_state {
|
||||
CMDLINE_DEV_STATE_NA = 0,
|
||||
CMDLINE_DEV_STATE_ON,
|
||||
CMDLINE_DEV_STATE_OFF,
|
||||
|
||||
CMDLINE_DEV_STATE_MAX,
|
||||
};
|
||||
|
||||
void dsmm_developer_proc_create(void);
|
||||
void dsmm_developer_proc_clean(void);
|
||||
|
||||
/**
|
||||
* get_developer_mode_state - Get developer state of the device.
|
||||
*
|
||||
* @state: State of the device.
|
||||
*
|
||||
* Returns the developer state, STATE_ON or STATE_OFF.
|
||||
*/
|
||||
int get_developer_mode_state(void);
|
||||
|
||||
#endif /* _DSMM_DEVELOPER_H */
|
||||
@@ -1,31 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef DSMM_DEVELOPER_H
|
||||
#define DSMM_DEVELOPER_H
|
||||
|
||||
#define DEVELOPER_STATUS_ON "true"
|
||||
#define DEVELOPER_STATUS_OFF "false"
|
||||
|
||||
enum build_variant {
|
||||
BUILD_VARIANT_USER = 0,
|
||||
BUILD_VARIANT_ENG,
|
||||
BUILD_VARIANT_MAX,
|
||||
};
|
||||
|
||||
enum developer_proc_status {
|
||||
DEVELOPER_PROC_STATUS_NA = 0,
|
||||
DEVELOPER_PROC_STATUS_ON,
|
||||
DEVELOPER_PROC_STATUS_OFF,
|
||||
DEVELOPER_PROC_STATUS_MAX,
|
||||
};
|
||||
|
||||
const char *developer_mode_state(void);
|
||||
|
||||
void dsmm_developer_proc_create(void);
|
||||
|
||||
void dsmm_developer_proc_clean(void);
|
||||
|
||||
#endif // DSMM_DEVELOPER_H
|
||||
@@ -1,19 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _XPM_MISC_H
|
||||
#define _XPM_MISC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct xpm_region_info {
|
||||
uint64_t addr_base;
|
||||
uint64_t length;
|
||||
};
|
||||
|
||||
int xpm_register_misc_device(void);
|
||||
void xpm_deregister_misc_device(void);
|
||||
|
||||
#endif /* _XPM_MISC_H */
|
||||
@@ -1,86 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _XPM_REPORT_H
|
||||
#define _XPM_REPORT_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
enum xpm_event_id {
|
||||
EVENT_INIT = 1011009110,
|
||||
EVENT_FILE = 1011009111,
|
||||
EVENT_MMAP = 1011009112,
|
||||
EVENT_INTEGRITY = 1011009113,
|
||||
};
|
||||
|
||||
enum xpm_event_type {
|
||||
TYPE_DEVICEFS_UNINIT = 0,
|
||||
TYPE_DEBUGFS_UNINIT,
|
||||
TYPE_DM_DISABLE,
|
||||
TYPE_FORMAT_UNDEF,
|
||||
TYPE_ANON_EXEC,
|
||||
TYPE_SIGN_INVALID,
|
||||
TYPE_DATA_MMAP_CODE,
|
||||
TYPE_INTEGRITY_RO,
|
||||
TYPE_INTEGRITY_WT,
|
||||
};
|
||||
|
||||
enum {
|
||||
TYPE_ABC,
|
||||
TYPE_ELF,
|
||||
TYPE_ANON,
|
||||
};
|
||||
|
||||
struct xpm_event_param {
|
||||
char *event_type;
|
||||
char *filename;
|
||||
ktime_t timestamp;
|
||||
pid_t pid;
|
||||
|
||||
struct vm_area_struct *vma;
|
||||
struct page *page;
|
||||
struct file *file;
|
||||
int code;
|
||||
unsigned long prot;
|
||||
};
|
||||
|
||||
struct xpm_event_info {
|
||||
char *event_type;
|
||||
enum xpm_event_id event_id;
|
||||
int (*set_content)(struct xpm_event_param *param, uint8_t *content,
|
||||
uint32_t content_len);
|
||||
};
|
||||
|
||||
#define MAX_CONTENT_LEN 900
|
||||
#define XPM_EVENT_VERSION 0
|
||||
|
||||
#ifndef CONFIG_SECURITY_XPM_DEBUG
|
||||
|
||||
#define xpm_report_ratelimited(func, fmt, ...) \
|
||||
do { \
|
||||
static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL, \
|
||||
DEFAULT_RATELIMIT_BURST); \
|
||||
if (__ratelimit(&_rs)) \
|
||||
func(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define xpm_report_ratelimited(func, fmt, ...) \
|
||||
func(fmt, ##__VA_ARGS__);
|
||||
|
||||
#endif
|
||||
|
||||
#define JSTR(val) "\""#val"\""
|
||||
#define JVAL_PAIR(val, format) JSTR(val) ": " #format
|
||||
#define JSTR_PAIR(val, format) JSTR(val) ": " JSTR(format)
|
||||
|
||||
void report_init_event(enum xpm_event_type type);
|
||||
void report_file_event(enum xpm_event_type type, struct file *file);
|
||||
void report_mmap_event(enum xpm_event_type type, struct vm_area_struct *vma,
|
||||
int code, int prot);
|
||||
void report_integrity_event(enum xpm_event_type type,
|
||||
struct vm_area_struct *vma, struct page *page);
|
||||
|
||||
#endif /* _XPM_REPORT_H */
|
||||
@@ -218,7 +218,7 @@ static bool dm_verity_is_enable(void)
|
||||
dm_verity_enable_check = true;
|
||||
if (!dm_verity_enable) {
|
||||
dm_partition_table[0].s_dev = get_root_partition_dev(&root_path);
|
||||
report_init_event(TYPE_DM_DISABLE);
|
||||
report_init_event(DM_DISABLE);
|
||||
}
|
||||
return dm_verity_enable;
|
||||
}
|
||||
@@ -246,7 +246,7 @@ static bool is_dm_verity(struct file *file)
|
||||
if (!dm_verity_enable_check) {
|
||||
dm_partition_table[0].s_dev = get_root_partition_dev(&root_path);
|
||||
dm_verity_enable_check = true;
|
||||
report_init_event(TYPE_DM_DISABLE);
|
||||
report_init_event(DM_DISABLE);
|
||||
}
|
||||
return dm_verity_check_for_path(file);
|
||||
}
|
||||
@@ -267,7 +267,7 @@ static bool is_fs_verity(struct file *file)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int check_exec_file_is_verity(struct file *file)
|
||||
static int check_exec_file_is_verity(struct file *file, bool is_exec)
|
||||
{
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
if (is_fs_verity(file))
|
||||
@@ -278,7 +278,7 @@ static int check_exec_file_is_verity(struct file *file)
|
||||
return FILE_SIGNATURE_DM_VERITY;
|
||||
|
||||
#ifdef CONFIG_SECURITY_CODE_SIGN
|
||||
if (!elf_file_enable_fs_verity(file))
|
||||
if (is_exec && !elf_file_enable_fs_verity(file))
|
||||
return FILE_SIGNATURE_FS_VERITY;
|
||||
#endif
|
||||
|
||||
@@ -531,8 +531,10 @@ need_parse:
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
ret = parse_elf_code_segment_info(file, &new_info);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
report_file_event(FORMAT_UNDEF, file);
|
||||
return ret;
|
||||
}
|
||||
#ifdef CONFIG_SECURITY_XPM_DEBUG
|
||||
test_print_info(file, type, new_info);
|
||||
#endif
|
||||
@@ -555,7 +557,7 @@ int get_exec_file_signature_info(struct file *file, bool is_exec,
|
||||
if (file == NULL || info_ptr == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
type = check_exec_file_is_verity(file);
|
||||
type = check_exec_file_is_verity(file, is_exec);
|
||||
return get_elf_code_segment_info(file, is_exec, type, info_ptr);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user