• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
4  */
5 #include <linux/string.h>
6 #include <linux/fcntl.h>
7 #include <linux/fs.h>
8 #include <linux/slab.h>
9 #include <linux/rwlock_types.h>
10 #include <linux/rwlock.h>
11 #include <linux/init.h>
12 #include <linux/moduleparam.h>
13 #include <linux/device-mapper.h>
14 #include <linux/kdev_t.h>
15 #include <linux/namei.h>
16 #include <linux/sched.h>
17 #include "mount.h"
18 #include "internal.h"
19 #include "exec_signature_info.h"
20 #include "xpm_report.h"
21 #include "xpm_log.h"
22 #include "code_sign_elf.h"
23 
24 #define VERITY_NODE_CACHE_LIMITS       10000
25 #define VERITY_NODE_CACHE_RECYCLE_NUM  200
26 
27 static DEFINE_RWLOCK(dm_verity_tree_lock);
28 static struct rb_root dm_verity_tree = RB_ROOT;
29 static int dm_verity_node_count;
30 static DEFINE_RWLOCK(fs_verity_tree_lock);
31 static struct rb_root fs_verity_tree = RB_ROOT;
32 static int fs_verity_node_count;
33 
34 struct verity_info {
35 	struct rb_root *root;
36 	rwlock_t *lock;
37 	int *node_count;
38 };
39 
40 static bool dm_verity_enable_check;
41 
42 #define DM_PARTITION_PATH_MAX	30
43 #define DM_VERITY_INVALID_DEV	((dev_t)-1)
44 
45 struct dm_partition {
46 	char path[DM_PARTITION_PATH_MAX];
47 	int len;
48 	dev_t s_dev;
49 };
50 
51 static struct dm_partition	dm_partition_table[] = {
52 	{ .path = "/",           .len = 1,  .s_dev = DM_VERITY_INVALID_DEV },
53 	{ .path = "/system/",    .len = 8,  .s_dev = DM_VERITY_INVALID_DEV },
54 	{ .path = "/vendor/",    .len = 8,  .s_dev = DM_VERITY_INVALID_DEV },
55 	{ .path = "/sys_prod/",  .len = 10, .s_dev = DM_VERITY_INVALID_DEV },
56 	{ .path = "/chip_prod/", .len = 11, .s_dev = DM_VERITY_INVALID_DEV },
57 	{ .path = "/vendor/modem/modem_vendor/", .len = 27, .s_dev = DM_VERITY_INVALID_DEV },
58 	{ .path = "/misc/",      .len = 6,  .s_dev = DM_VERITY_INVALID_DEV },
59 };
60 
61 static struct path	root_path;
62 
get_file_dev(struct file * file)63 static dev_t get_file_dev(struct file *file)
64 {
65 	struct super_block *sb;
66 	struct mount *mnt;
67 
68 	if (file->f_path.mnt == NULL)
69 		return DM_VERITY_INVALID_DEV;
70 
71 	mnt = container_of(file->f_path.mnt, struct mount, mnt);
72 	sb = mnt->mnt.mnt_sb;
73 	if (sb == NULL)
74 		return DM_VERITY_INVALID_DEV;
75 
76 	return sb->s_dev;
77 }
78 
get_partition_dev_form_mnt(const struct vfsmount * mnt)79 static dev_t get_partition_dev_form_mnt(const struct vfsmount *mnt)
80 {
81 	if (IS_ERR_OR_NULL(mnt) || IS_ERR_OR_NULL(mnt->mnt_sb))
82 		return DM_VERITY_INVALID_DEV;
83 	return mnt->mnt_sb->s_dev;
84 }
85 
get_root_partition_dev(struct path * root_path)86 static dev_t get_root_partition_dev(struct path *root_path)
87 {
88 	int ret;
89 	struct path path;
90 	struct path *mount_path = &path;
91 	dev_t s_dev;
92 
93 	if (root_path != NULL)
94 		mount_path = root_path;
95 	ret = kern_path("/", LOOKUP_DIRECTORY, mount_path);
96 	if (ret) {
97 		xpm_log_error("get / path failed.");
98 		return DM_VERITY_INVALID_DEV;
99 	}
100 	s_dev = get_partition_dev_form_mnt(mount_path->mnt);
101 	if (s_dev == DM_VERITY_INVALID_DEV || root_path == NULL)
102 		path_put(mount_path);
103 	xpm_log_info("get / dev=%u:%u success", s_dev, MINOR(s_dev));
104 	return s_dev;
105 }
106 
get_dm_verity_partition_dev(const char * dir,struct path * root_path)107 static dev_t get_dm_verity_partition_dev(const char *dir, struct path *root_path)
108 {
109 	int ret;
110 	struct path path;
111 	dev_t s_dev;
112 
113 	ret = vfs_path_lookup(root_path->dentry, root_path->mnt, dir, LOOKUP_DIRECTORY, &path);
114 	if (ret) {
115 		xpm_log_error("get %s path failed", dir);
116 		return DM_VERITY_INVALID_DEV;
117 	}
118 	s_dev = get_partition_dev_form_mnt(path.mnt);
119 	path_put(&path);
120 	xpm_log_info("get %s dev=%u:%u success", dir, s_dev, MINOR(s_dev));
121 	return s_dev;
122 }
123 
find_partition_dev(struct file * file,dev_t s_dev)124 static bool find_partition_dev(struct file *file, dev_t s_dev)
125 {
126 	char *full_path = NULL;
127 	char path[PATH_MAX] = {0};
128 	struct dm_partition *dm_path;
129 	int i;
130 
131 	for (i = 1; i < sizeof(dm_partition_table) / sizeof(struct dm_partition); i++) {
132 		dm_path = &dm_partition_table[i];
133 		if (dm_path->s_dev != DM_VERITY_INVALID_DEV)
134 			continue;
135 
136 		if (full_path == NULL) {
137 			full_path = file_path(file, path, PATH_MAX-1);
138 			if (IS_ERR(full_path))
139 				return false;
140 		}
141 		if (strncmp(dm_path->path, full_path, dm_path->len) != 0)
142 			continue;
143 
144 		dm_path->s_dev = get_dm_verity_partition_dev(dm_path->path, &root_path);
145 		if (dm_path->s_dev == DM_VERITY_INVALID_DEV)
146 			return false;
147 		if (dm_path->s_dev == s_dev)
148 			return true;
149 		return false;
150 	}
151 
152 	return false;
153 }
154 
dm_verity_check_for_path(struct file * file)155 static bool dm_verity_check_for_path(struct file *file)
156 {
157 	static int system_startup_stage;
158 	struct dm_partition *dm_path;
159 	dev_t s_dev, root_dev;
160 	int i;
161 
162 	s_dev = get_file_dev(file);
163 	for (i = 0; i < sizeof(dm_partition_table) / sizeof(struct dm_partition); i++) {
164 		dm_path = &dm_partition_table[i];
165 		if (dm_path->s_dev == s_dev)
166 			return true;
167 	}
168 
169 	if (system_startup_stage)
170 		return find_partition_dev(file, s_dev);
171 
172 	if (task_pid_nr(current) == 1) {
173 		root_dev = get_root_partition_dev(NULL);
174 		if (root_dev != dm_partition_table[0].s_dev) {
175 			path_put(&root_path);
176 			dm_partition_table[0].s_dev = get_root_partition_dev(&root_path);
177 			for (i = 1; i < sizeof(dm_partition_table) / sizeof(struct dm_partition); i++)
178 				dm_partition_table[i].s_dev = DM_VERITY_INVALID_DEV;
179 			system_startup_stage = 1;
180 		}
181 	}
182 	return find_partition_dev(file, s_dev);
183 }
184 
185 #ifdef CONFIG_DM_VERITY
186 #define HVB_CMDLINE_VB_STATE	"ohos.boot.hvb.enable"
187 static bool dm_verity_enable;
188 
hvb_boot_param_cb(char * param,char * val,const char * unused,void * arg)189 static int hvb_boot_param_cb(char *param, char *val,
190 	const char *unused, void *arg)
191 {
192 	if (param == NULL || val == NULL)
193 		return 0;
194 
195 	if (strcmp(param, HVB_CMDLINE_VB_STATE) != 0)
196 		return 0;
197 
198 	if (strcmp(val, "true") == 0 || strcmp(val, "green") == 0)
199 		dm_verity_enable = true;
200 
201 	return 0;
202 }
203 
dm_verity_is_enable(void)204 static bool dm_verity_is_enable(void)
205 {
206 	char *cmdline;
207 
208 	if (dm_verity_enable || dm_verity_enable_check)
209 		return dm_verity_enable;
210 
211 	cmdline = kstrdup(saved_command_line, GFP_KERNEL);
212 	if (cmdline == NULL)
213 		return false;
214 
215 	parse_args("hvb.enable params", cmdline, NULL,
216 		0, 0, 0, NULL, &hvb_boot_param_cb);
217 	kfree(cmdline);
218 	dm_verity_enable_check = true;
219 	if (!dm_verity_enable) {
220 		dm_partition_table[0].s_dev = get_root_partition_dev(&root_path);
221 		report_init_event(DM_DISABLE);
222 	}
223 	return dm_verity_enable;
224 }
225 
dm_verity_check_for_mnt(struct file * file)226 static bool dm_verity_check_for_mnt(struct file *file)
227 {
228 	struct mapped_device *device;
229 
230 	device = dm_get_md(get_file_dev(file));
231 	if (device == NULL)
232 		return false;
233 
234 	dm_put(device);
235 	return true;
236 }
237 #endif
238 
is_dm_verity(struct file * file)239 static bool is_dm_verity(struct file *file)
240 {
241 #ifdef CONFIG_DM_VERITY
242 	if (dm_verity_is_enable())
243 		return dm_verity_check_for_mnt(file);
244 #endif
245 
246 	if (!dm_verity_enable_check) {
247 		dm_partition_table[0].s_dev = get_root_partition_dev(&root_path);
248 		dm_verity_enable_check = true;
249 		report_init_event(DM_DISABLE);
250 	}
251 	return dm_verity_check_for_path(file);
252 }
253 
254 #ifdef CONFIG_FS_VERITY
is_fs_verity(struct file * file)255 static bool is_fs_verity(struct file *file)
256 {
257 	struct inode *file_node;
258 
259 	file_node = file_inode(file);
260 	if (file_node == NULL)
261 		return false;
262 
263 	if (file_node->i_verity_info == NULL)
264 		return false;
265 
266 	return true;
267 }
268 #endif
269 
check_exec_file_is_verity(struct file * file,bool is_exec)270 static int check_exec_file_is_verity(struct file *file, bool is_exec)
271 {
272 #ifdef CONFIG_FS_VERITY
273 	if (is_fs_verity(file))
274 		return FILE_SIGNATURE_FS_VERITY;
275 #endif
276 
277 	if (is_dm_verity(file))
278 		return FILE_SIGNATURE_DM_VERITY;
279 
280 #ifdef CONFIG_SECURITY_CODE_SIGN
281 	if (is_exec && !elf_file_enable_fs_verity(file))
282 		return FILE_SIGNATURE_FS_VERITY;
283 #endif
284 
285 	return FILE_SIGNATURE_INVALID;
286 }
287 
rb_search_node(struct rb_root * root,uintptr_t file_inode)288 static struct exec_file_signature_info *rb_search_node(struct rb_root *root, uintptr_t file_inode)
289 {
290 	struct rb_node *node = root->rb_node;
291 	struct exec_file_signature_info *file_node;
292 
293 	while (node != NULL) {
294 		file_node = rb_entry(node, struct exec_file_signature_info, rb_node);
295 		if (file_inode < file_node->inode) {
296 			node = file_node->rb_node.rb_left;
297 		} else if (file_inode > file_node->inode) {
298 			node = file_node->rb_node.rb_right;
299 		} else {
300 			atomic_inc(&file_node->reference);
301 			return file_node;
302 		}
303 	}
304 	return NULL;
305 }
306 
rb_add_node(struct rb_root * root,int * node_count,struct exec_file_signature_info * node)307 static struct exec_file_signature_info *rb_add_node(struct rb_root *root, int *node_count,
308 	struct exec_file_signature_info *node)
309 {
310 	struct rb_node **p = &root->rb_node;
311 	struct rb_node *parent = NULL;
312 	struct exec_file_signature_info *file;
313 
314 	while (*p != NULL) {
315 		parent = *p;
316 		file = rb_entry(parent, struct exec_file_signature_info, rb_node);
317 		if (node->inode < file->inode) {
318 			p = &(*p)->rb_left;
319 		} else if (node->inode > file->inode) {
320 			p = &(*p)->rb_right;
321 		} else {
322 			atomic_inc(&file->reference);
323 			return file;
324 		}
325 	}
326 
327 	rb_link_node(&node->rb_node, parent, p);
328 	rb_insert_color(&node->rb_node, root);
329 	atomic_inc(&node->reference);
330 	(*node_count)++;
331 	return NULL;
332 }
333 
rb_erase_node(struct rb_root * root,int * node_count,struct exec_file_signature_info * node)334 static void rb_erase_node(struct rb_root *root, int *node_count,
335 	struct exec_file_signature_info *node)
336 {
337 	rb_erase(&node->rb_node, root);
338 	(*node_count)--;
339 }
340 
find_idle_nodes(struct rb_root * root,uintptr_t * ilde_nodes,int count)341 static int find_idle_nodes(struct rb_root *root, uintptr_t *ilde_nodes, int count)
342 {
343 	int i = 0;
344 	struct exec_file_signature_info *code_segment;
345 	struct rb_node *node;
346 
347 	for (node = rb_first(root); node != NULL && i < count; node = rb_next(node)) {
348 		code_segment = rb_entry(node, struct exec_file_signature_info, rb_node);
349 		if (atomic_read(&code_segment->reference) > 0)
350 			continue;
351 
352 		ilde_nodes[i++] = (uintptr_t)code_segment;
353 	}
354 	return i;
355 }
356 
clear_code_segment_info_cache(struct rb_root * root,int * node_count)357 static void clear_code_segment_info_cache(struct rb_root *root, int *node_count)
358 {
359 	struct exec_file_signature_info *code_segment_info;
360 	uintptr_t *code_segments;
361 	int i = 0;
362 	int count = VERITY_NODE_CACHE_RECYCLE_NUM;
363 
364 	code_segments = kcalloc(count, sizeof(uintptr_t), GFP_KERNEL);
365 	if (code_segments == NULL)
366 		return;
367 
368 	count = find_idle_nodes(root, code_segments, count);
369 	while (i < count) {
370 		code_segment_info = (struct exec_file_signature_info *)code_segments[i];
371 		rb_erase_node(root, node_count, code_segment_info);
372 		kfree(code_segment_info);
373 		i++;
374 	}
375 	kfree(code_segments);
376 }
377 
378 #ifdef CONFIG_SECURITY_XPM_DEBUG
test_elf_code_segment_info_size(struct rb_root * root)379 static size_t test_elf_code_segment_info_size(struct rb_root *root)
380 {
381 	size_t size = 0;
382 	struct exec_file_signature_info *file_node;
383 	struct rb_node *node;
384 
385 	for (node = rb_first(root); node != NULL; node = rb_next(node)) {
386 		file_node = rb_entry(node, struct exec_file_signature_info, rb_node);
387 		size += sizeof(struct exec_file_signature_info) +
388 				file_node->code_segment_count * sizeof(struct exec_segment_info);
389 	}
390 	return size;
391 }
392 
test_printf_code_segment_cache_size(void)393 static void test_printf_code_segment_cache_size(void)
394 {
395 	size_t cache_size = 0;
396 	int dm_count;
397 	int fs_count;
398 
399 	read_lock(&dm_verity_tree_lock);
400 	cache_size += test_elf_code_segment_info_size(&dm_verity_tree);
401 	dm_count = dm_verity_node_count;
402 	read_unlock(&dm_verity_tree_lock);
403 
404 	read_lock(&fs_verity_tree_lock);
405 	cache_size += test_elf_code_segment_info_size(&fs_verity_tree);
406 	fs_count = fs_verity_node_count;
407 	read_unlock(&fs_verity_tree_lock);
408 
409 	xpm_log_info("cache dm count=%d, fs count=%d, cache size=%d KB\n", dm_count, fs_count, cache_size / 1024);
410 }
411 
test_print_info(struct file * file,unsigned int type,const struct exec_file_signature_info * file_info)412 static void test_print_info(struct file *file, unsigned int type, const struct exec_file_signature_info *file_info)
413 {
414 	char *full_path;
415 	char path[PATH_MAX] = {0};
416 	static int code_segment_test_count = 100;
417 	int i;
418 
419 	code_segment_test_count--;
420 	if (code_segment_test_count > 0)
421 		return;
422 
423 	full_path = file_path(file, path, PATH_MAX - 1);
424 	if (IS_ERR(full_path))
425 		return;
426 
427 	for (i = 0; i < file_info->code_segment_count; i++)
428 		xpm_log_info("%s -> type: %s, info: offset=0x%llx size=0x%lx\n",
429 			full_path, type == FILE_SIGNATURE_DM_VERITY ? "dm" : "fs",
430 			file_info->code_segments->file_offset, file_info->code_segments->size);
431 
432 	code_segment_test_count = 100;
433 }
434 #endif
435 
rm_code_segment_info(void)436 static void rm_code_segment_info(void)
437 {
438 	if (dm_verity_node_count + fs_verity_node_count < VERITY_NODE_CACHE_LIMITS)
439 		return;
440 
441 #ifdef CONFIG_SECURITY_XPM_DEBUG
442 	test_printf_code_segment_cache_size();
443 #endif
444 
445 	if (dm_verity_node_count > fs_verity_node_count) {
446 		write_lock(&dm_verity_tree_lock);
447 		clear_code_segment_info_cache(&dm_verity_tree, &dm_verity_node_count);
448 		write_unlock(&dm_verity_tree_lock);
449 		return;
450 	}
451 
452 	write_lock(&fs_verity_tree_lock);
453 	clear_code_segment_info_cache(&fs_verity_tree, &fs_verity_node_count);
454 	write_unlock(&fs_verity_tree_lock);
455 }
456 
get_verity_info(int type,struct verity_info * verity)457 static int get_verity_info(int type, struct verity_info *verity)
458 {
459 	if (type == FILE_SIGNATURE_DM_VERITY) {
460 		verity->root = &dm_verity_tree;
461 		verity->lock = &dm_verity_tree_lock;
462 		verity->node_count = &dm_verity_node_count;
463 		return 0;
464 	}
465 
466 	if (type == FILE_SIGNATURE_FS_VERITY) {
467 		verity->lock = &fs_verity_tree_lock;
468 		verity->root = &fs_verity_tree;
469 		verity->node_count = &fs_verity_node_count;
470 		return 0;
471 	}
472 
473 	return -EINVAL;
474 }
475 
insert_new_signature_info(struct inode * file_node,int type,struct verity_info * verity,struct exec_file_signature_info * new_info,struct exec_file_signature_info ** old_info)476 static void insert_new_signature_info(struct inode *file_node, int type,
477 	struct verity_info *verity, struct exec_file_signature_info *new_info, struct exec_file_signature_info **old_info)
478 {
479 	new_info->type = (unsigned int)type;
480 	new_info->inode = (uintptr_t)file_node;
481 	RB_CLEAR_NODE(&new_info->rb_node);
482 	if ((*old_info) != NULL) {
483 		write_lock(verity->lock);
484 		rb_erase_node(verity->root, verity->node_count, *old_info);
485 		(*old_info)->type |= FILE_SIGNATURE_DELETE;
486 		write_unlock(verity->lock);
487 		if (atomic_sub_return(1, &(*old_info)->reference) <= 0) {
488 			kfree(*old_info);
489 			*old_info = NULL;
490 		}
491 	}
492 
493 	write_lock(verity->lock);
494 	*old_info = rb_add_node(verity->root, verity->node_count, new_info);
495 	write_unlock(verity->lock);
496 }
497 
get_elf_code_segment_info(struct file * file,bool is_exec,int type,struct exec_file_signature_info ** code_segment_info)498 static int get_elf_code_segment_info(struct file *file, bool is_exec, int type,
499 	struct exec_file_signature_info **code_segment_info)
500 {
501 	int ret;
502 	struct verity_info verity;
503 	struct inode *file_node;
504 	struct exec_file_signature_info *new_info;
505 	struct exec_file_signature_info *old_info;
506 
507 	if (get_verity_info(type, &verity) < 0)
508 		return -EINVAL;
509 
510 	file_node = file_inode(file);
511 	if (file_node == NULL)
512 		return -EINVAL;
513 
514 	read_lock(verity.lock);
515 	old_info = rb_search_node(verity.root, (uintptr_t)file_node);
516 	read_unlock(verity.lock);
517 	if (old_info != NULL) {
518 		if (is_exec && old_info->code_segments == NULL)
519 			goto need_parse;
520 
521 		*code_segment_info = old_info;
522 		return 0;
523 	}
524 
525 need_parse:
526 	rm_code_segment_info();
527 
528 	if (!is_exec) {
529 		new_info = kzalloc(sizeof(struct exec_file_signature_info), GFP_KERNEL);
530 		if (new_info == NULL)
531 			return -ENOMEM;
532 	} else {
533 		ret = parse_elf_code_segment_info(file, &new_info);
534 		if (ret < 0) {
535 			report_file_event(FORMAT_UNDEF, file);
536 			return ret;
537 		}
538 #ifdef CONFIG_SECURITY_XPM_DEBUG
539 		test_print_info(file, type, new_info);
540 #endif
541 	}
542 
543 	insert_new_signature_info(file_node, type, &verity, new_info, &old_info);
544 	if (old_info != NULL) {
545 		kfree(new_info);
546 		new_info = old_info;
547 	}
548 	*code_segment_info = new_info;
549 	return 0;
550 }
551 
get_exec_file_signature_info(struct file * file,bool is_exec,struct exec_file_signature_info ** info_ptr)552 int get_exec_file_signature_info(struct file *file, bool is_exec,
553 	struct exec_file_signature_info **info_ptr)
554 {
555 	int type;
556 
557 	if (file == NULL || info_ptr == NULL)
558 		return -EINVAL;
559 
560 	type = check_exec_file_is_verity(file, is_exec);
561 	return get_elf_code_segment_info(file, is_exec, type, info_ptr);
562 }
563 
put_exec_file_signature_info(struct exec_file_signature_info * exec_info)564 int put_exec_file_signature_info(struct exec_file_signature_info *exec_info)
565 {
566 	if ((exec_info == NULL) ||
567 		!exec_file_signature_is_verity(exec_info))
568 		return -EINVAL;
569 
570 	if (atomic_sub_return(1, &exec_info->reference) <= 0 &&
571 		exec_file_signature_is_delete(exec_info))
572 		kfree(exec_info);
573 	return 0;
574 }
575 
elf_code_segment_info_delete(struct rb_root * root,int * node_count,struct inode * file_node)576 static struct exec_file_signature_info *elf_code_segment_info_delete(struct rb_root *root,
577 	int *node_count, struct inode *file_node)
578 {
579 	struct exec_file_signature_info *signature_info;
580 
581 	signature_info = rb_search_node(root, (uintptr_t)file_node);
582 	if (signature_info != NULL) {
583 		rb_erase_node(root, node_count, signature_info);
584 		if (atomic_sub_return(1, &signature_info->reference) > 0)
585 			signature_info->type |= FILE_SIGNATURE_DELETE;
586 		else
587 			kfree(signature_info);
588 	}
589 	return signature_info;
590 }
591 
delete_exec_file_signature_info(struct inode * file_node)592 void delete_exec_file_signature_info(struct inode *file_node)
593 {
594 	struct exec_file_signature_info *signature_info;
595 
596 	if (file_node == NULL)
597 		return;
598 
599 	write_lock(&fs_verity_tree_lock);
600 	signature_info = elf_code_segment_info_delete(&fs_verity_tree, &fs_verity_node_count, file_node);
601 	write_unlock(&fs_verity_tree_lock);
602 	if (signature_info != NULL)
603 		return;
604 
605 	write_lock(&dm_verity_tree_lock);
606 	signature_info = elf_code_segment_info_delete(&dm_verity_tree, &dm_verity_node_count, file_node);
607 	write_unlock(&dm_verity_tree_lock);
608 }
609