diff --git a/code_sign/Makefile b/code_sign/Makefile index 894ea26ad788bec815fa9567de0968eea49bb976..6ff03802edc8f3aebea6e964a6be8e6db74e199a 100644 --- a/code_sign/Makefile +++ b/code_sign/Makefile @@ -5,12 +5,14 @@ obj-$(CONFIG_SECURITY_CODE_SIGN) += \ code_sign_misc.o \ verify_cert_chain.o \ code_sign_ioctl.o \ + code_sign_elf.o \ code_sign_ext.o ccflags-$(CONFIG_SECURITY_CODE_SIGN) += \ -I$(srctree)/fs/code_sign \ -I$(srctree)/security/selinux/include \ - -I$(srctree)/security/selinux + -I$(srctree)/security/selinux \ + -I$(srctree)/security/xpm/include $(addprefix $(obj)/,$(obj-y)): $(obj)/flask.h diff --git a/code_sign/code_sign_elf.c b/code_sign/code_sign_elf.c new file mode 100644 index 0000000000000000000000000000000000000000..674f0e93cd287c0dddbf4e9ae6dc961f343630b0 --- /dev/null +++ b/code_sign/code_sign_elf.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#include +#include +#include + +#include "dsmm_developer.h" +#include "code_sign_elf.h" +#include "code_sign_log.h" + +#define SIGN_HEAD_SIZE (sizeof(sign_head_t)) + +static void parse_sign_head(sign_head_t *out, char *ptr) +{ + sign_head_t *tmp_data = (sign_head_t *) ptr; + /* magic and version are in byte represention */ + strncpy(out->magic, tmp_data->magic, sizeof(tmp_data->magic)); + strncpy(out->version, tmp_data->version, sizeof(tmp_data->version)); + out->sign_data_size = le32_to_cpu(tmp_data->sign_data_size); + out->sign_block_num = le32_to_cpu(tmp_data->sign_block_num); + out->padding = le32_to_cpu(tmp_data->padding); +} + +static void parse_tl_hdr(tl_header_t *out, char *ptr) +{ + tl_header_t *tmp_data = (tl_header_t *) ptr; + out->type = le32_to_cpu(tmp_data->type); + out->length = le32_to_cpu(tmp_data->length); +} + +static void parse_block_hdr(block_hdr_t *out, char *ptr) +{ + block_hdr_t *tmp = (block_hdr_t *) ptr; + out->type = le32_to_cpu(tmp->type); + out->length = le32_to_cpu(tmp->length); + out->offset = le32_to_cpu(tmp->offset); +} + +static int get_block_headers(sign_block_t *sign_block, char *sign_data_ptr) +{ + /* parse all block headers */ + for (int i = 0; i < sign_block->sign_head.sign_block_num; i++) { + block_hdr_t *tmp_block_hdr = (block_hdr_t *) (sign_data_ptr + sizeof(block_hdr_t) * i); + if (BLOCK_TYPE_CODE_SIGNING == le32_to_cpu(tmp_block_hdr->type)) { + parse_block_hdr(&sign_block->code_signing_block_hdr, sign_data_ptr + sizeof(block_hdr_t) * i); + } else if (BLOCK_TYPE_SIGNED_PROFILE == le32_to_cpu(tmp_block_hdr->type)) { + parse_block_hdr(&sign_block->profile_block_hdr, sign_data_ptr + sizeof(block_hdr_t) * i); + } else { + code_sign_log_error("block type invalid: %u", le32_to_cpu(tmp_block_hdr->type)); + } + } + if (sign_block->code_signing_block_hdr.type != BLOCK_TYPE_CODE_SIGNING) { + code_sign_log_error("code signing block header not exist"); + return -EINVAL; + } + if (sign_block->code_signing_block_hdr.offset + sizeof(tl_header_t) > sign_block->sign_head.sign_data_size) { + code_sign_log_error("code signing block offset invalid: %u", sign_block->code_signing_block_hdr.offset); + return -EINVAL; + } + return 0; +} + +static int get_merkle_tree(sign_block_t *sign_block, char *sign_data_ptr) +{ + parse_tl_hdr(&sign_block->merkle_tree_hdr, sign_data_ptr + sign_block->code_signing_block_hdr.offset); + if (sign_block->merkle_tree_hdr.type != TYPE_MERKLE_TREE) { + code_sign_log_error("merkle tree type invalid: %u", sign_block->merkle_tree_hdr.type); + return -EINVAL; + } + if (sign_block->merkle_tree_hdr.length + sizeof(tl_header_t) + > sign_block->sign_head.sign_data_size - sign_block->code_signing_block_hdr.offset - sizeof(tl_header_t)) { + code_sign_log_error("merkle tree data length invalid: %u", sign_block->merkle_tree_hdr.length); + return -EINVAL; + } + return 0; +} + +static int get_fsverity_desc(sign_block_t *sign_block, char *sign_data_ptr) +{ + /* parse fsverity header and fsverity descriptor */ + parse_tl_hdr(&sign_block->fsverity_desc_hdr, sign_data_ptr + sign_block->code_signing_block_hdr.offset + + sizeof(tl_header_t) + sign_block->merkle_tree_hdr.length); + if (sign_block->fsverity_desc_hdr.type != TYPE_FS_VERITY_DESC) { + code_sign_log_error("fsverity desc type invalid: %u", sign_block->fsverity_desc_hdr.type); + return -EINVAL; + } + if (sign_block->fsverity_desc_hdr.length + > sign_block->sign_head.sign_data_size - sign_block->code_signing_block_hdr.offset + - sizeof(tl_header_t) - sign_block->merkle_tree_hdr.length - sizeof(tl_header_t)) { + code_sign_log_error("fsverity desc length invalid: %u", sign_block->fsverity_desc_hdr.length); + return -EINVAL; + } + + sign_block->fsverity_desc = (fs_verity_desc_t *) (sign_data_ptr + sign_block->code_signing_block_hdr.offset + + sizeof(tl_header_t) + sign_block->merkle_tree_hdr.length + + sizeof(tl_header_t)); + return 0; +} + +static int enable_by_sign_head(struct file *fp, long long fsize, char *sign_head_ptr) +{ + sign_block_t sign_block; + memset(&sign_block, 0, sizeof(sign_block)); + + parse_sign_head(&sign_block.sign_head, sign_head_ptr); + loff_t sign_data_start = fsize - SIGN_HEAD_SIZE - sign_block.sign_head.sign_data_size; + + /* parse code signing block header */ + char *sign_data_ptr = kzalloc(sign_block.sign_head.sign_data_size, GFP_KERNEL); + if (!sign_data_ptr) { + code_sign_log_error("kzalloc of sign_data_ptr failed"); + return -ENOMEM; + } + ssize_t cnt = vfs_read(fp, sign_data_ptr, sign_block.sign_head.sign_data_size, &sign_data_start); + if (cnt != sign_block.sign_head.sign_data_size) { + code_sign_log_error("read sign data from file failed: read value %lu, expect %u bytes", + cnt, sign_block.sign_head.sign_data_size); + goto out; + } + int err = get_block_headers(&sign_block, sign_data_ptr); + if (err) { + code_sign_log_error("get_block_headers failed, err: %d", err); + goto out; + } + + err = get_merkle_tree(&sign_block, sign_data_ptr); + if (err) { + code_sign_log_error("get_merkle_tree failed, err: %d", err); + goto out; + } + + /* compute length of padding before merkle tree data */ + merkle_tree_t merkle_tree; + merkle_tree.padding_length = sign_block.merkle_tree_hdr.length & ((1 << PAGE_SIZE_4K) - 1); + merkle_tree.merkle_tree_data = sign_data_ptr + sign_block.code_signing_block_hdr.offset + + sizeof(tl_header_t) + merkle_tree.padding_length; + merkle_tree.merkle_tree_length = sign_block.merkle_tree_hdr.length - merkle_tree.padding_length; + sign_block.merkle_tree = &merkle_tree; + + err = get_fsverity_desc(&sign_block, sign_data_ptr); + if (err) { + code_sign_log_error("get_fsverity_desc failed, err: %d", err); + goto out; + } + + /* fsverity_enable_with_descriptor in fs/verity/enable.c */ + err = fsverity_enable_with_descriptor(fp, (void *)(sign_block.fsverity_desc), sign_block.fsverity_desc_hdr.length); + if (err) { + code_sign_log_error("fsverity_enable_with_descriptor returns err: %d", err); + goto out; + } + +out: + kfree(sign_data_ptr); + return err; +} + +int elf_file_enable_fs_verity(struct file *file) +{ + /* developer mode */ + if (strcmp(developer_mode_state(), DEVELOPER_STATUS_ON)) { + code_sign_log_info("developer mode off, elf not allowed to execute"); + return -EINVAL; + } + mm_segment_t fs; + char *path_buf = kzalloc(PATH_MAX, GFP_KERNEL); + if (!path_buf) { + code_sign_log_error("alloc mem for path_buf failed"); + return -ENOMEM; + } + int err = 0; + char *real_path = file_path(file, path_buf, PATH_MAX - 1); + if (!real_path) { + code_sign_log_error("get file path failed"); + err = -EFAULT; + goto release_path_buf_out; + } + + struct file *fp = filp_open(real_path, O_RDONLY, 0); + if (!fp) { + code_sign_log_error("filp_open failed"); + err = -EFAULT; + goto release_path_buf_out; + } + struct inode *inode = file_inode(fp); + if (!inode) { + code_sign_log_error("file_inode failed"); + err = -EFAULT; + goto filp_close_out;; + } + + long long fsize = inode->i_size; + long long pos = 0; + if (fsize <= SIGN_HEAD_SIZE) { + code_sign_log_error("file size too small: %llu", fsize); + err = -EINVAL; + goto filp_close_out; + } else { + pos = fsize - SIGN_HEAD_SIZE; + } + + char *sign_head_ptr = kzalloc(SIGN_HEAD_SIZE, GFP_KERNEL); + if (!sign_head_ptr) { + code_sign_log_error("kzalloc of sign_head_ptr failed"); + err = -ENOMEM; + goto filp_close_out; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + + ssize_t cnt = vfs_read(fp, sign_head_ptr, SIGN_HEAD_SIZE, &pos); + if (cnt != SIGN_HEAD_SIZE) { + code_sign_log_error("read sign head from file failed: return value %lu, expect %u bytes", + cnt, SIGN_HEAD_SIZE); + err = -EFAULT; + goto release_sign_head_out; + } + sign_head_t *tmp_sign_head = (sign_head_t *)sign_head_ptr; + + /* check magic string */ + if (strncmp(tmp_sign_head->magic, SIGN_MAGIC_STR, sizeof(SIGN_MAGIC_STR) - 1) != 0) { + code_sign_log_error("enable fsverity on file %s failed: magic string not found", real_path); + err = -EINVAL; + goto release_sign_head_out; + } + if (fsize < (SIGN_HEAD_SIZE + le32_to_cpu(tmp_sign_head->sign_data_size))) { + code_sign_log_error("sign data size invalid: %u", tmp_sign_head->sign_data_size); + err = -EINVAL; + goto release_sign_head_out; + } + + err = enable_by_sign_head(fp, fsize, sign_head_ptr); + if (err) { + code_sign_log_error("enable_by_sign_head err: %d", err); + goto release_sign_head_out; + } + code_sign_log_info("enable fsverity on file %s success", real_path); + +release_sign_head_out: + kfree(sign_head_ptr); + set_fs(fs); +filp_close_out: + filp_close(fp, NULL); +release_path_buf_out: + kfree(path_buf); + return err; +} diff --git a/code_sign/code_sign_elf.h b/code_sign/code_sign_elf.h new file mode 100644 index 0000000000000000000000000000000000000000..3348f9fa48bc3c8ad97a7292557c1128495f10ce --- /dev/null +++ b/code_sign/code_sign_elf.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#ifndef _CODE_SIGN_ELF_H +#define _CODE_SIGN_ELF_H + +#include + +#define PAGE_SIZE_4K 12 + +/* + * Sign block of ELF file consists of + * sign data and sign head + * + * Detailed structure: + * +-------------------------------------------------+ + * | |type (4 bytes)| code signing| + * | |length (4 bytes)| block | + * | |offset (4 bytes)| header | + * | +---------------------------+-------------| + * | |type (4 bytes)| profile | + * | |length (4 bytes)| block | + * | |offset (4 bytes)| header | + * | +---------------------------+-------------| + * | | .. other block headers .. | | + * | +---------------------------+-------------| + * | SIGN |type (4 bytes)| merkle | + * | |length (4 bytes)| tree | + * | DATA |merkle tree data (N bytes)| block | + * | +---------------------------+-------------| + * | |type (4 bytes)| | + * | |length (4 bytes)| | + * | |version (1 byte )| | + * | |hash alg (1 byte )| | + * | |log2blocksize (1 byte )| | + * | |salt size (1 byte )| | + * | |signature size (4 bytes)| fs verity | + * | |data size (8 bytes)| block | + * | |root hash (64 bytes)| | + * | |salt (32 bytes)| | + * | |flags (4 bytes)| | + * | |reserved (4 bytes)| | + * | |tree offset (8 bytes)| | + * | |reserved (127 bytes)| | + * | |cs version (1 byte )| | + * | |signature (N bytes)| | + * |-------+---------------------------+-------------| + * | | magic string (16 bytes)| | + * | SIGN | version (4 bytes) | | + * | | sign data size (4 bytes) | | + * | HEAD | sign block num (4 bytes) | | + * | | padding (4 bytes) | | + * +-------+-----------------------------------------+ + */ + +static const __u32 MAGIC_STRING_LEN = 16; +static const char SIGN_MAGIC_STR[] = "elf sign block "; + +enum CODE_SIGNING_DATA_TYPE { + TYPE_FS_VERITY_DESC = 0x1, + TYPE_MERKLE_TREE = 0x2 +}; + +enum BLOCK_TYPE { + BLOCK_TYPE_UNSIGNED_PROFILE = 0x1, + BLOCK_TYPE_SIGNED_PROFILE = 0x2, + BLOCK_TYPE_CODE_SIGNING = 0x3 +}; + +#pragma pack(push, 1) +typedef struct +{ + __u8 magic[16]; + __u8 version[4]; + __u32 sign_data_size; + __u32 sign_block_num; + __u32 padding; +} sign_head_t; + +typedef struct +{ + __u32 type; + __u32 length; +} tl_header_t; + + +typedef struct +{ + __u32 type; + __u32 length; + __u32 offset; +} block_hdr_t; + +typedef struct +{ + __u8 version; + __u8 hash_algorithm; + __u8 log2_block_size; + __u8 salt_size; + __u32 signature_size; + __u64 data_size; + __u8 root_hash[64]; + __u8 salt[32]; + __u32 flags; + __u32 reserved; + __u64 tree_offset; + __u8 reserved_buf[127]; + __u8 cs_version; + char signature[]; +} fs_verity_desc_t; + +#pragma pack(pop) + +typedef struct +{ + __u32 padding_length; + char *merkle_tree_data; + __u32 merkle_tree_length; +} merkle_tree_t; + +typedef struct +{ + /* sign data */ + block_hdr_t code_signing_block_hdr; + block_hdr_t profile_block_hdr; + /* code signing block */ + tl_header_t merkle_tree_hdr; + merkle_tree_t *merkle_tree; + tl_header_t fsverity_desc_hdr; + fs_verity_desc_t *fsverity_desc; + + /* sign head */ + sign_head_t sign_head; +} sign_block_t; + +int elf_file_enable_fs_verity(struct file *file); + +#endif /* _CODE_SIGN_ELF_H */ diff --git a/xpm/Makefile b/xpm/Makefile index 36494c3b693b3220829bd83dbf7eb1e30f57bf21..ae717fb726afa3719b7169f504ec7f9bcc248842 100755 --- a/xpm/Makefile +++ b/xpm/Makefile @@ -21,7 +21,8 @@ ccflags-$(CONFIG_SECURITY_XPM) += \ -I$(srctree)/security/xpm/include \ -I$(srctree)/security/selinux/include \ -I$(srctree)/security/selinux \ - -I$(srctree)/fs + -I$(srctree)/fs \ + -I$(srctree)/fs/code_sign $(addprefix $(obj)/,$(obj-y)): $(obj)/flask.h diff --git a/xpm/validator/exec_signature_info.c b/xpm/validator/exec_signature_info.c index 17faa68999fdc25fd20f4f54de8d5f5a93b37f99..897efd680975030ce285a1337efaec5484f36d49 100644 --- a/xpm/validator/exec_signature_info.c +++ b/xpm/validator/exec_signature_info.c @@ -18,6 +18,7 @@ #include "exec_signature_info.h" #include "xpm_report.h" #include "xpm_log.h" +#include "code_sign_elf.h" #define VERITY_NODE_CACHE_LIMITS 10000 #define VERITY_NODE_CACHE_RECYCLE_NUM 200 @@ -272,6 +273,11 @@ static int check_exec_file_is_verity(struct file *file) if (is_dm_verity(file)) return FILE_SIGNATURE_DM_VERITY; +#ifdef CONFIG_SECURITY_CODE_SIGN + if (!elf_file_enable_fs_verity(file)) + return FILE_SIGNATURE_FS_VERITY; +#endif + return FILE_SIGNATURE_INVALID; }