diff -drupN a/security/fivm/fivm_main.c b/security/fivm/fivm_main.c --- a/security/fivm/fivm_main.c 1970-01-01 03:00:00.000000000 +0300 +++ b/security/fivm/fivm_main.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2014 Allwinner Ltd. + * + * Author: + * Ryan Chen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: fiv_main.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int fivm_debug; +#include "fivm.h" +static int fivm_initialized; +static struct FILE_SIG *file_sig_table; +static struct FILE_SIG_HEAD *file_sig_head; +static const char ROOT[] = "/system"; + +DEFINE_MUTEX(_fid_mutex); + +#ifdef FIVM_DEBUG_TIMMING +#include +static void fivm_gettime(const char *str) +{ + struct timeval t; + do_gettimeofday(&t); + printk("[FIVM timer]%s: %lu.%lu\n", str, t.tv_sec, t.tv_usec); +} +#else +static void fivm_gettime(const char *str) +{ + return; +} +#endif + +static char _p_dir[1024]; +static int __walk_to_root(struct file *file, char *pathname, + struct fivm_path *search_fp) +{ + struct dentry *p; + char *start, *f_path; + int rc = -1 ; + int loop, len; + + mutex_lock(&_fid_mutex); + p = file->f_path.dentry; + + start = _p_dir + sizeof(_p_dir) - 1;/*Reserved a null for string end*/ + memset(_p_dir, 0, sizeof(_p_dir)); + for (loop = 0; loop < 16 && p; loop++, p = p->d_parent) { + if (*(p->d_name.name) == '/') + break; + else { + len = strnlen(p->d_name.name, MAX_NAME_LEN); + start -= len; + memcpy(start, p->d_name.name, len); + *(--start) = '/'; + } + } + + dprintk("FIVM find root path : %s\n", start); + + if (!memcmp(start, ROOT, sizeof(ROOT) - 1)) { + len = strnlen(start, FILE_NAME_LEN); + f_path = kmalloc(len, GFP_KERNEL); + if (!f_path) { + derr("Out of memory\n"); + goto out; + } + memcpy(f_path, start, len); + search_fp->path = f_path; + search_fp->flag = FIVM_PART_PATH ; + rc = 0; + } +out: + mutex_unlock(&_fid_mutex); + return rc; +} + +static int _find_dir(struct file *file, char *pathname, + struct fivm_path *search_fp) +{ + if (unlikely(!pathname)) { + printk("Null file path\n"); + return -1; + } + + if (likely(*pathname == '/')) + do { + if (!memcmp(pathname, ROOT, sizeof(ROOT) - 1)) { + search_fp->path = pathname; + search_fp->flag = FIVM_FULL_PATH; + dprintk("Root File path: %s\n", pathname); + return 0; + } + } while (0); + else { + return __walk_to_root(file, pathname, search_fp); + } + return -1; +} + +/*Incremental crc table*/ +static int _cmp(const void *a, const void *b) +{ + const int *fa = a; + const struct FILE_SIG *fb = b; + + if (*fa < fb->crc) + return -1; + if (*fa > fb->crc) + return 1; + return 0; +} + +static struct FILE_SIG *_find_file(char *file_name) +{ + struct FILE_SIG *fs; + int crc, slen; + size_t num, size; + void *base; + + base = file_sig_table; + num = file_sig_head->actual_cnt ; + size = sizeof(struct FILE_SIG); + + slen = strnlen(file_name, FILE_NAME_LEN); + crc = ~crc32_le(~0, file_name, slen); + dprintk("Find_file debug: name %s, crc 0x%x", file_name, crc); + + fs = bsearch(&crc, base, num, size, _cmp); + if (!fs) { + derr("Find file %s crc fail\n", file_name); + return NULL; + } + + if (strncmp(file_name, fs->name, slen)) { + derr("Find file %s name fail\n", file_name); + return NULL; + } + + return fs; +} + +static void rel_fivm_path(struct fivm_path *fp) +{ + if (fp->flag == FIVM_PART_PATH && + fp->path) + kfree(fp->path); +} + +static int process_verify(struct file *file, char *pathname) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct FILE_SIG *fs; + int rc = 0; + char digest[SHA_DIG_MAX]; + + struct fivm_path search_fp = {NULL, 0}; + fivm_gettime("Enter process verication"); + + if (!fivm_initialized || !S_ISREG(inode->i_mode)) + return 0; + + rc = _find_dir(file, pathname, &search_fp); + if (rc != 0) { + return 0; /*Nothing to do, Just go on */ + } + + fivm_gettime("Find directory name "); + fs = _find_file(search_fp.path); + if (!fs) { + derr("Can't find file %s in sha table\n", search_fp.path); + goto out; + } + if (fs->flag) { + dprintk("Had verified\n"); + goto out; + } + + fivm_gettime("Find file name "); + memset(digest, 0, SHA_DIG_MAX); + rc = fivm_calc_hash(file, digest); + if (rc) { + derr("fivm_calc_hash fail\n"); + goto out ; + } + + fivm_gettime("Calc file digest "); + if (memcmp(digest, fs->sha, SHA_DIG_MAX)) { + derr("Sha compare fail\n"); + derr("Local calculate:\n"); + print_hex_dump_bytes("", DUMP_PREFIX_NONE, + digest, SHA_DIG_MAX); + derr("Store value:\n"); + print_hex_dump_bytes("", DUMP_PREFIX_NONE, + fs->sha, SHA_DIG_MAX); + rc = -1; + goto out; + } + fs->flag = 1; + rc = 0; +out: + rel_fivm_path(&search_fp); + return rc; +} + + +/** + * fivm_file_mmap + * @file: pointer to the file to be verify + * @prot: contains the protection that will be applied by the kernel. + * + * Return 0 on success, an error code on failure. + */ +static int _fivm_mmap_verify(struct file *file, unsigned long prot) +{ + int rc; + if (!file) + return 0; + + rc = process_verify(file, (char *)file->f_path.dentry->d_name.name); + return rc; +} + +/** + * fivm_open_verify - verify file against to the hash table + * @file: pointer to the file to be verify + * @mask: + * + * Return 0 on success, an error code on failure. + */ +static int _fivm_open_verify(struct file *file, const char *pathname, int mask) +{ + int rc; + if (!file || !pathname) + return 0; + + rc = process_verify(file, (char *)pathname); + return rc; +} + +#ifdef FIVM_LKM_DEBUG +static int _file_meta_read( + char *filename, + char *buf, + ssize_t len, + int offset + ) +{ + struct file *fd; + int retlen = -1; + mm_segment_t old_fs; + + if (!filename || !buf) { + derr("- filename/buf NULL\n"); + return (-EINVAL); + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fd = filp_open(filename, O_RDONLY, 0); + + if (IS_ERR(fd)) { + derr(" -file open fail\n"); + return -1; + } + do {if ((fd->f_op == NULL) || (fd->f_op->read == NULL)) { + derr(" -file can't to open!!\n"); + break; + } + + if (fd->f_pos != offset) { + if (fd->f_op->llseek) { + if (fd->f_op->llseek(fd, offset, 0) != offset) { + derr(" -failed to seek!!\n"); + break; + } + } else { + fd->f_pos = offset; + } + } + + retLen = fd->f_op->read(fd, buf, len, &fd->f_pos); + } while (false); + + filp_close(fd, NULL); + set_fs(old_fs); + return retlen; +} + +static const char *sig_table = "/dev/block/by-name/verity_block"; +static int read_file_hash_table(void) +{ + int cnt, rlen, + rc = -1, + offset; + + file_sig_head = kzalloc(sizeof(*file_sig_head), GFP_KERNEL); + if (!file_sig_head) { + derr(" out of memory\n"); + return -ENOMEM; + } + + rlen = sizeof(struct FILE_SIG_HEAD); + offset = sizeof(struct FILE_LIST_HEAD); + rc = _file_meta_read( + (char *)sig_table, + (char *)file_sig_head, + rlen, + offset); + if (rc != rlen || file_sig_head->magic != FILE_SIG_MAGIC) { + derr("Read file %s fail\n", sig_table); + rc = -EIO; + goto out; + } + + cnt = file_sig_head->actual_cnt; + if (cnt > DIR_MAX_FILE_NUM) { + derr("out of files count 0x%x\n", cnt); + rc = -1; + goto out; + } + + rlen = sizeof(struct FILE_SIG)*cnt; + file_sig_table = kzalloc(rlen, GFP_KERNEL); + if (!file_sig_table) { + derr(" out of memory\n"); + rc = -ENOMEM ; + goto out; + } + offset += sizeof(*file_sig_head); + rc = _file_meta_read( + (char *)sig_table, + (char *)file_sig_table, + rlen, + offset + ); + + if (rc != rlen) { + derr("Read file %s fail\n", sig_table); + rc = -EIO; + goto out; + } + + rc = 0; +out: + if (file_sig_table) + kfree(file_sig_table); + if (file_sig_head) + kfree(file_sig_head); + return rc; + +} +#endif + +extern int fivm_register_func( + int (*mmap_verify)(struct file *, unsigned long), + int (*open_verify)(struct file *, const char *, int)); +extern int fivm_unregister_func(void); + +raw_spinlock_t fivm_hook_lock; +int fivm_enable(void) +{ +#ifdef FIVM_LKM_DEBUG + int rc; + rc = read_file_hash_table(); + if (rc < 0) { + derr("read hash table fail\n"); + return -1; + } +#endif + raw_spin_lock(&fivm_hook_lock); + fivm_register_func(_fivm_mmap_verify, _fivm_open_verify); + fivm_initialized = 1; + raw_spin_unlock(&fivm_hook_lock); + + pr_info("FIVM: enable fivm done\n"); + return 0; +} + +int fivm_disable(void) +{ + raw_spin_lock(&fivm_hook_lock); + fivm_unregister_func(); + fivm_initialized = 0; + raw_spin_unlock(&fivm_hook_lock); + return 0 ; +} + +/*Get fivm table from user*/ +int fivm_set(void *arg) +{ + struct fivm_param param; + void *sh, *st; + unsigned int sh_size, st_size; + + derr("fivm_set trace arg: %p\n", arg); + + if (!access_ok(VERIFY_READ, arg, sizeof(param))) { + derr("fivm param error\n"); + return -EFAULT; + } + + if (copy_from_user(¶m, (void __user *)arg, sizeof(param))) { + derr("fivm_set get param failed\n"); + return -EFAULT; + } + + sh = param.sig_head; + sh_size = param.sig_head_size; + st = param.sig_table; + st_size = param.sig_table_size; + + if (sh_size != sizeof(*file_sig_head) || + st_size > FILE_NAME_LEN * DIR_MAX_FILE_NUM) { + derr("fivm set buf size error %d:%d\n", + sh_size, st_size); + return -EFAULT; + } + + file_sig_head = kzalloc(sh_size, GFP_KERNEL); + if (!file_sig_head || file_sig_table) { + derr("out of memory \n"); + return -EFAULT; + } + + if (copy_from_user(file_sig_head, (void __user *)sh, sh_size)) { + derr("fivm sig head buffer from user fail\n"); + kfree(file_sig_head); + return -EFAULT; + } + + if (st_size != file_sig_head->actual_cnt * sizeof(struct FILE_SIG)) { + derr("wrong fivm sig table count\n"); + kfree(file_sig_head); + return -EFAULT; + } + file_sig_table = kzalloc(st_size, GFP_KERNEL); + if (!file_sig_table) { + derr("out of memory file_sig_table\n"); + kfree(file_sig_head); + return -EFAULT; + } + + if (copy_from_user(file_sig_table, (void __user *)st, st_size)) { + derr("fivm sig table buffer from user fail\n"); + kfree(file_sig_head); + kfree(file_sig_table); + return -EFAULT; + } + + if (fivm_debug == 1) { + print_hex_dump_bytes("fivm io sig head buf:", DUMP_PREFIX_NONE, + file_sig_head, sh_size); + print_hex_dump_bytes("fivm io sig table buf:", DUMP_PREFIX_NONE, + file_sig_table, 16 * sizeof(struct FILE_SIG)); + } + return 0; +} + +int fivm_init(void) +{ + pr_info("FIVM init\n"); + raw_spin_lock_init(&fivm_hook_lock); + return 0; +} + +int fivm_cleanup(void) +{ + fivm_disable(); + if (file_sig_head) + kfree(file_sig_head); + if (file_sig_table) + kfree(file_sig_table); + return 0 ; +}