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