1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2023 Huawei Device Co., Ltd. 4 */ 5 6#include <asm/byteorder.h> 7#include <linux/version.h> 8#include <linux/fsverity.h> 9#include <linux/slab.h> 10 11#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) 12#include <linux/pagemap.h> 13#endif 14 15#include "code_sign_elf.h" 16#include "code_sign_log.h" 17#include "verify_cert_chain.h" 18 19#ifdef CONFIG_SECURITY_XPM 20#include "dsmm_developer.h" 21#endif 22 23#define SIGN_HEAD_SIZE (sizeof(sign_head_t)) 24 25static void parse_sign_head(sign_head_t *out, char *ptr) 26{ 27 sign_head_t *tmp_data = (sign_head_t *) ptr; 28 /* magic and version are in byte represention */ 29 strncpy(out->magic, tmp_data->magic, sizeof(tmp_data->magic)); 30 strncpy(out->version, tmp_data->version, sizeof(tmp_data->version)); 31 out->sign_data_size = le32_to_cpu(tmp_data->sign_data_size); 32 out->sign_block_num = le32_to_cpu(tmp_data->sign_block_num); 33 out->padding = le32_to_cpu(tmp_data->padding); 34} 35 36static void parse_tl_hdr(tl_header_t *out, char *ptr) 37{ 38 tl_header_t *tmp_data = (tl_header_t *) ptr; 39 out->type = le32_to_cpu(tmp_data->type); 40 out->length = le32_to_cpu(tmp_data->length); 41} 42 43static void parse_block_hdr(block_hdr_t *out, char *ptr) 44{ 45 block_hdr_t *tmp = (block_hdr_t *) ptr; 46 out->type = le32_to_cpu(tmp->type); 47 out->length = le32_to_cpu(tmp->length); 48 out->offset = le32_to_cpu(tmp->offset); 49} 50 51static int get_block_headers(sign_block_t *sign_block, char *sign_data_ptr) 52{ 53 /* parse all block headers */ 54 for (int i = 0; i < sign_block->sign_head.sign_block_num; i++) { 55 block_hdr_t *tmp_block_hdr = (block_hdr_t *) (sign_data_ptr + sizeof(block_hdr_t) * i); 56 if (BLOCK_TYPE_CODE_SIGNING == le32_to_cpu(tmp_block_hdr->type)) { 57 parse_block_hdr(&sign_block->code_signing_block_hdr, sign_data_ptr + sizeof(block_hdr_t) * i); 58 } else if (BLOCK_TYPE_SIGNED_PROFILE == le32_to_cpu(tmp_block_hdr->type)) { 59 parse_block_hdr(&sign_block->profile_block_hdr, sign_data_ptr + sizeof(block_hdr_t) * i); 60 } else { 61 code_sign_log_error("block type invalid: %u", le32_to_cpu(tmp_block_hdr->type)); 62 } 63 } 64 if (sign_block->code_signing_block_hdr.type != BLOCK_TYPE_CODE_SIGNING) { 65 code_sign_log_error("code signing block header not exist"); 66 return -EINVAL; 67 } 68 if (sign_block->code_signing_block_hdr.offset + sizeof(tl_header_t) > sign_block->sign_head.sign_data_size) { 69 code_sign_log_error("code signing block offset invalid: %u", sign_block->code_signing_block_hdr.offset); 70 return -EINVAL; 71 } 72 return 0; 73} 74 75static int get_merkle_tree(sign_block_t *sign_block, char *sign_data_ptr) 76{ 77 parse_tl_hdr(&sign_block->merkle_tree_hdr, sign_data_ptr + sign_block->code_signing_block_hdr.offset); 78 if (sign_block->merkle_tree_hdr.type != TYPE_MERKLE_TREE) { 79 code_sign_log_error("merkle tree type invalid: %u", sign_block->merkle_tree_hdr.type); 80 return -EINVAL; 81 } 82 if (sign_block->merkle_tree_hdr.length + sizeof(tl_header_t) 83 > sign_block->sign_head.sign_data_size - sign_block->code_signing_block_hdr.offset - sizeof(tl_header_t)) { 84 code_sign_log_error("merkle tree data length invalid: %u", sign_block->merkle_tree_hdr.length); 85 return -EINVAL; 86 } 87 return 0; 88} 89 90static int get_fsverity_desc(sign_block_t *sign_block, char *sign_data_ptr) 91{ 92 /* parse fsverity header and fsverity descriptor */ 93 parse_tl_hdr(&sign_block->fsverity_desc_hdr, sign_data_ptr + sign_block->code_signing_block_hdr.offset 94 + sizeof(tl_header_t) + sign_block->merkle_tree_hdr.length); 95 if (sign_block->fsverity_desc_hdr.type != TYPE_FS_VERITY_DESC) { 96 code_sign_log_error("fsverity desc type invalid: %u", sign_block->fsverity_desc_hdr.type); 97 return -EINVAL; 98 } 99 if (sign_block->fsverity_desc_hdr.length 100 > sign_block->sign_head.sign_data_size - sign_block->code_signing_block_hdr.offset 101 - sizeof(tl_header_t) - sign_block->merkle_tree_hdr.length - sizeof(tl_header_t)) { 102 code_sign_log_error("fsverity desc length invalid: %u", sign_block->fsverity_desc_hdr.length); 103 return -EINVAL; 104 } 105 106 sign_block->fsverity_desc = (struct code_sign_descriptor *) (sign_data_ptr + sign_block->code_signing_block_hdr.offset 107 + sizeof(tl_header_t) + sign_block->merkle_tree_hdr.length 108 + sizeof(tl_header_t)); 109 return 0; 110} 111 112static int validate_elf_source(const struct code_sign_descriptor *desc) 113{ 114 const u32 sig_size = le32_to_cpu(desc->sig_size); 115 int ret = 0; 116 117 code_sign_verify_certchain(desc->signature, sig_size, NULL, &ret); 118 if (ret < 0) 119 return ret; 120 121 if (ret <= DEBUG_CODE_START || ret >= DEBUG_CODE_END || ret == DEBUG_DEVELOPER_CODE) { 122 code_sign_log_error("invalid elf source, type: %d", ret); 123 return -EKEYREJECTED; 124 } 125 return 0; 126} 127 128static int enable_by_sign_head(struct file *fp, struct inode *inode, long long fsize, char *sign_head_ptr) 129{ 130 sign_block_t sign_block; 131 memset(&sign_block, 0, sizeof(sign_block)); 132 133 parse_sign_head(&sign_block.sign_head, sign_head_ptr); 134 loff_t sign_data_start = fsize - SIGN_HEAD_SIZE - sign_block.sign_head.sign_data_size; 135 136 /* parse code signing block header */ 137 char *sign_data_ptr = kzalloc(sign_block.sign_head.sign_data_size, GFP_KERNEL); 138 if (!sign_data_ptr) { 139 code_sign_log_error("kzalloc of sign_data_ptr failed"); 140 return -ENOMEM; 141 } 142 ssize_t cnt = vfs_read(fp, sign_data_ptr, sign_block.sign_head.sign_data_size, &sign_data_start); 143 if (cnt != sign_block.sign_head.sign_data_size) { 144 code_sign_log_error("read sign data from file failed: read value %lu, expect %u bytes", 145 cnt, sign_block.sign_head.sign_data_size); 146 goto out; 147 } 148 int err = get_block_headers(&sign_block, sign_data_ptr); 149 if (err) { 150 code_sign_log_error("get_block_headers failed, err: %d", err); 151 goto out; 152 } 153 154 err = get_merkle_tree(&sign_block, sign_data_ptr); 155 if (err) { 156 code_sign_log_error("get_merkle_tree failed, err: %d", err); 157 goto out; 158 } 159 160 /* compute length of padding before merkle tree data */ 161 merkle_tree_t merkle_tree; 162 merkle_tree.padding_length = sign_block.merkle_tree_hdr.length & ((1 << PAGE_SIZE_4K) - 1); 163 merkle_tree.merkle_tree_data = sign_data_ptr + sign_block.code_signing_block_hdr.offset 164 + sizeof(tl_header_t) + merkle_tree.padding_length; 165 merkle_tree.merkle_tree_length = sign_block.merkle_tree_hdr.length - merkle_tree.padding_length; 166 sign_block.merkle_tree = &merkle_tree; 167 168 err = get_fsverity_desc(&sign_block, sign_data_ptr); 169 if (err) { 170 code_sign_log_error("get_fsverity_desc failed, err: %d", err); 171 goto out; 172 } 173 174 err = mnt_want_write_file(fp); 175 if (err) /* -EROFS */ 176 goto out; 177 178 err = deny_write_access(fp); 179 if (err) /* -ETXTBSY */ 180 goto out_drop_write; 181 182 /* validate cert chain of elf signer */ 183 err = validate_elf_source(sign_block.fsverity_desc); 184 if (err) 185 goto out; 186 187 /* fsverity_enable_with_descriptor in fs/verity/enable.c */ 188 err = fsverity_enable_with_descriptor(fp, (void *)(sign_block.fsverity_desc), sign_block.fsverity_desc_hdr.length); 189 if (err) { 190 code_sign_log_error("fsverity_enable_with_descriptor returns err: %d", err); 191 goto out_allow_write_access; 192 } 193 194 filemap_write_and_wait(inode->i_mapping); 195 invalidate_inode_pages2(inode->i_mapping); 196 197out_allow_write_access: 198 allow_write_access(fp); 199out_drop_write: 200 mnt_drop_write_file(fp); 201out: 202 kfree(sign_data_ptr); 203 return err; 204} 205 206int elf_file_enable_fs_verity(struct file *file) 207{ 208#ifdef CONFIG_SECURITY_XPM 209 /* developer mode */ 210 if (get_developer_mode_state() != STATE_ON) { 211 code_sign_log_info("developer mode off, elf not allowed to execute"); 212 return -EINVAL; 213 } 214#else 215 code_sign_log_info("developer mode off, elf not allowed to execute"); 216 return -EINVAL; 217#endif 218 219#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) 220 mm_segment_t fs; 221#endif 222 char *path_buf = kzalloc(PATH_MAX, GFP_KERNEL); 223 if (!path_buf) { 224 code_sign_log_error("alloc mem for path_buf failed"); 225 return -ENOMEM; 226 } 227 int err = 0; 228 char *real_path = file_path(file, path_buf, PATH_MAX - 1); 229 if (IS_ERR_OR_NULL(real_path)) { 230 code_sign_log_error("get file path failed"); 231 err = -ENOENT; 232 goto release_path_buf_out; 233 } 234 235 struct file *fp = filp_open(real_path, O_RDONLY, 0); 236 if (IS_ERR(fp)) { 237 code_sign_log_error("filp_open failed"); 238 err = PTR_ERR(fp); 239 goto release_path_buf_out; 240 } 241 struct inode *inode = file_inode(fp); 242 if (!inode) { 243 code_sign_log_error("file_inode failed"); 244 err = -EFAULT; 245 goto filp_close_out;; 246 } 247 248 long long fsize = inode->i_size; 249 long long pos = 0; 250 if (fsize <= SIGN_HEAD_SIZE) { 251 code_sign_log_error("file size too small: %llu", fsize); 252 err = -EINVAL; 253 goto filp_close_out; 254 } else { 255 pos = fsize - SIGN_HEAD_SIZE; 256 } 257 258 char *sign_head_ptr = kzalloc(SIGN_HEAD_SIZE, GFP_KERNEL); 259 if (!sign_head_ptr) { 260 code_sign_log_error("kzalloc of sign_head_ptr failed"); 261 err = -ENOMEM; 262 goto filp_close_out; 263 } 264#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) 265 fs = get_fs(); 266 set_fs(KERNEL_DS); 267#endif 268 ssize_t cnt = vfs_read(fp, sign_head_ptr, SIGN_HEAD_SIZE, &pos); 269 if (cnt != SIGN_HEAD_SIZE) { 270 code_sign_log_error("read sign head from file failed: return value %lu, expect %u bytes", 271 cnt, SIGN_HEAD_SIZE); 272 err = -EFAULT; 273 goto release_sign_head_out; 274 } 275 sign_head_t *tmp_sign_head = (sign_head_t *)sign_head_ptr; 276 277 /* check magic string */ 278 if (strncmp(tmp_sign_head->magic, SIGN_MAGIC_STR, sizeof(SIGN_MAGIC_STR) - 1) != 0) { 279 err = -EINVAL; 280 goto release_sign_head_out; 281 } 282 if (fsize < (SIGN_HEAD_SIZE + le32_to_cpu(tmp_sign_head->sign_data_size))) { 283 code_sign_log_error("sign data size invalid: %u", tmp_sign_head->sign_data_size); 284 err = -EINVAL; 285 goto release_sign_head_out; 286 } 287 288 err = enable_by_sign_head(fp, inode, fsize, sign_head_ptr); 289 if (err) { 290 code_sign_log_error("enable_by_sign_head err: %d", err); 291 goto release_sign_head_out; 292 } 293 code_sign_log_info("enable fsverity on file %s success", real_path); 294 295release_sign_head_out: 296 kfree(sign_head_ptr); 297#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) 298 set_fs(fs); 299#endif 300filp_close_out: 301 filp_close(fp, NULL); 302release_path_buf_out: 303 kfree(path_buf); 304 return err; 305} 306