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
parse_sign_head(sign_head_t * out,char * ptr)25 static 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
parse_tl_hdr(tl_header_t * out,char * ptr)36 static 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
parse_block_hdr(block_hdr_t * out,char * ptr)43 static 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
get_block_headers(sign_block_t * sign_block,char * sign_data_ptr)51 static 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
get_merkle_tree(sign_block_t * sign_block,char * sign_data_ptr)75 static 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
get_fsverity_desc(sign_block_t * sign_block,char * sign_data_ptr)90 static 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
validate_elf_source(const struct code_sign_descriptor * desc)112 static 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
enable_by_sign_head(struct file * fp,struct inode * inode,long long fsize,char * sign_head_ptr)128 static 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
197 out_allow_write_access:
198 allow_write_access(fp);
199 out_drop_write:
200 mnt_drop_write_file(fp);
201 out:
202 kfree(sign_data_ptr);
203 return err;
204 }
205
elf_file_enable_fs_verity(struct file * file)206 int 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
295 release_sign_head_out:
296 kfree(sign_head_ptr);
297 #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
298 set_fs(fs);
299 #endif
300 filp_close_out:
301 filp_close(fp, NULL);
302 release_path_buf_out:
303 kfree(path_buf);
304 return err;
305 }
306