| /* |
| * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/fs.h> |
| #include <linux/file.h> |
| #include <linux/kernel.h> |
| #include <linux/kobject.h> |
| #include <linux/slab.h> |
| #include <linux/string.h> |
| #include <linux/syscalls.h> |
| #include <linux/sysfs.h> |
| #include "include/defex_debug.h" |
| #include "include/defex_internal.h" |
| #include "include/defex_rules.h" |
| |
| #ifdef DEFEX_USE_PACKED_RULES |
| #if defined(DEFEX_KERNEL_ONLY) && defined(DEFEX_INTEGRITY_ENABLE) |
| #else |
| #include "defex_packed_rules.inc" |
| #endif |
| #endif |
| |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| #include <linux/fs.h> |
| #include <crypto/hash.h> |
| #include <crypto/public_key.h> |
| #include <crypto/internal/rsa.h> |
| #include "../integrity/integrity.h" |
| #define SHA256_DIGEST_SIZE 32 |
| #ifdef DEFEX_KERNEL_ONLY |
| #define RULES "/system/etc/defex_packed_rules.bin" |
| unsigned char *defex_packed_rules; |
| #endif /* DEFEX_KERNEL_ONLY */ |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| |
| static struct kset *defex_kset; |
| |
| |
| int check_system_mount(void) |
| { |
| static int mount_system_root = -1; |
| struct file *fp; |
| |
| if (mount_system_root < 0) { |
| fp = filp_open("/sbin/recovery", O_RDONLY, 0); |
| if (IS_ERR(fp)) { |
| printk(KERN_ALERT "[DEFEX] normal mode\n"); |
| mount_system_root = 0; |
| } else { |
| printk(KERN_ALERT "[DEFEX] recovery mode\n"); |
| filp_close(fp, NULL); |
| fp = filp_open("/system_root", O_DIRECTORY | O_PATH, 0); |
| if (IS_ERR(fp)) { |
| printk(KERN_ALERT "[DEFEX] system_root=FALSE\n"); |
| mount_system_root = 0; |
| } else { |
| printk(KERN_ALERT "[DEFEX] system_root=TRUE\n"); |
| filp_close(fp, NULL); |
| mount_system_root = 1; |
| } |
| } |
| } |
| return (mount_system_root > 0); |
| } |
| |
| static void parse_static_rules(const struct static_rule *rules, size_t max_len, int rules_number) |
| { |
| int i; |
| size_t count; |
| const char *current_rule; |
| #if (defined(DEFEX_PERMISSIVE_PED) || defined(DEFEX_PERMISSIVE_SP)) |
| static const char permissive[2] = "2"; |
| #endif /* DEFEX_PERMISSIVE_**/ |
| |
| for (i = 0; i < rules_number; i++) { |
| count = strnlen(rules[i].rule, max_len); |
| current_rule = rules[i].rule; |
| switch (rules[i].feature_type) { |
| #ifdef DEFEX_PED_ENABLE |
| case feature_ped_status: |
| #ifdef DEFEX_PERMISSIVE_PED |
| current_rule = permissive; |
| #endif /* DEFEX_PERMISSIVE_PED */ |
| task_defex_privesc_store_status(global_privesc_obj, NULL, current_rule, count); |
| break; |
| #endif /* DEFEX_PED_ENABLE */ |
| #ifdef DEFEX_SAFEPLACE_ENABLE |
| case feature_safeplace_status: |
| #ifdef DEFEX_PERMISSIVE_SP |
| current_rule = permissive; |
| #endif /* DEFEX_PERMISSIVE_SP */ |
| safeplace_status_store(global_safeplace_obj, NULL, current_rule, count); |
| break; |
| #endif /* DEFEX_SAFEPLACE_ENABLE */ |
| } |
| } |
| |
| printk(KERN_INFO "DEFEX_LSM started"); |
| } |
| |
| #ifdef DEFEX_USE_PACKED_RULES |
| struct rule_item_struct *lookup_dir(struct rule_item_struct *base, const char *name, int l) |
| { |
| struct rule_item_struct *item = NULL; |
| unsigned int offset; |
| |
| if (!base || !base->next_level) |
| return item; |
| item = GET_ITEM_PTR(base->next_level); |
| do { |
| if (item->size == l && !memcmp(name, item->name, l)) return item; |
| offset = item->next_file; |
| item = GET_ITEM_PTR(offset); |
| } while(offset); |
| return NULL; |
| } |
| |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| int defex_check_integrity(struct file *f, unsigned char *hash) |
| { |
| struct crypto_shash *handle = NULL; |
| struct shash_desc* shash = NULL; |
| unsigned char hash_sha256[SHA256_DIGEST_SIZE]; |
| unsigned char *buff = NULL; |
| size_t buff_size = PAGE_SIZE; |
| loff_t file_size = 0; |
| int ret = 0, err = 0, read_size = 0; |
| int i = 0; //TEST |
| |
| if (IS_ERR(f)) |
| goto hash_error; |
| |
| handle = crypto_alloc_shash("sha256", 0, 0); |
| if (IS_ERR(handle)) |
| goto hash_error; |
| |
| shash = kzalloc(sizeof(struct shash_desc) + crypto_shash_descsize(handle), GFP_KERNEL); |
| if (NULL == shash) |
| goto hash_error; |
| |
| shash->flags = 0; |
| shash->tfm = handle; |
| |
| buff = kmalloc(buff_size, GFP_KERNEL); |
| if (NULL == buff) |
| goto hash_error; |
| |
| err = crypto_shash_init(shash); |
| if (err < 0) |
| goto hash_error; |
| |
| |
| while(1) { |
| read_size = integrity_kernel_read(f, file_size, (char*)buff, buff_size); |
| if (0 > read_size) |
| goto hash_error; |
| if (0 == read_size) |
| break; |
| file_size += read_size; |
| err = crypto_shash_update(shash, buff, read_size); |
| if (err < 0) |
| goto hash_error; |
| } |
| |
| err = crypto_shash_final(shash, hash_sha256); |
| if (err < 0) |
| goto hash_error; |
| |
| ret = memcmp(hash_sha256, hash, SHA256_DIGEST_SIZE); |
| |
| /* TEST */ |
| if (ret){ |
| for (i = 0; i < 32; i++){ |
| printk("%02x%02x : %d", hash_sha256[i], hash[i], ret); |
| } |
| } |
| goto hash_exit; |
| |
| hash_error: |
| ret = -1; |
| hash_exit: |
| if (buff) |
| kfree(buff); |
| if (shash) |
| kfree(shash); |
| if (handle) |
| crypto_free_shash(handle); |
| return ret; |
| |
| } |
| |
| int defex_integrity_default(const char *file_path) |
| { |
| static const char integrity_default[] = "/system/bin/install-recovery.sh"; |
| return strncmp(integrity_default, file_path, sizeof(integrity_default)); |
| } |
| |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| |
| #if defined DEFEX_INTEGRITY_ENABLE && defined DEFEX_KERNEL_ONLY |
| int defex_read_rules(const char *path, unsigned char **data) |
| { |
| struct file *file; |
| loff_t size; |
| unsigned char *buf; |
| int rc = -EINVAL; |
| mm_segment_t old_fs; |
| |
| if (!path || !*path) |
| return -EINVAL; |
| |
| old_fs = get_fs(); |
| set_fs(get_ds()); |
| file = filp_open(path, O_RDONLY, 0); |
| set_fs(old_fs); |
| if (IS_ERR(file)) { |
| rc = PTR_ERR(file); |
| pr_err("[DEFEX] Unable to open file: %s (%d)", path, rc); |
| return rc; |
| } |
| |
| size = i_size_read(file_inode(file)); |
| if (size <= 0) |
| goto out; |
| |
| buf = kmalloc(size, GFP_KERNEL); |
| if (!buf) { |
| rc = -ENOMEM; |
| goto out; |
| } |
| |
| rc = integrity_kernel_read(file, 0, buf, size); |
| if (rc == size) { |
| *data = buf; |
| } else { |
| kfree(buf); |
| if (rc >= 0) |
| rc = -EIO; |
| } |
| out: |
| fput(file); |
| return rc; |
| } |
| |
| #endif /* defined DEFEX_INTEGRITY_ENABLE && defined DEFEX_KERNEL_ONLY */ |
| |
| int lookup_tree(const char *file_path, int attribute, struct file *f) |
| { |
| const char *ptr, *next_separator; |
| struct rule_item_struct *base, *cur_item = NULL; |
| int l; |
| |
| if (!file_path || *file_path != '/') |
| return 0; |
| |
| |
| /* load packed binary rules during the first-time access */ |
| #if defined DEFEX_INTEGRITY_ENABLE && defined DEFEX_KERNEL_ONLY |
| if (!defex_packed_rules) { |
| defex_read_rules(RULES, &defex_packed_rules); |
| if (!defex_packed_rules) { |
| printk("[DEFEX] Rules loading Failed, process: %s\n", file_path); |
| /* allow all while filesystem is not ready */ |
| return 1; |
| } else { |
| printk("[DEFEX] Rules loading OK, process: %s\n", file_path); |
| } |
| } |
| #endif /* defined DEFEX_INTEGRITY_ENABLE && defined DEFEX_KERNEL_ONLY */ |
| |
| base = (struct rule_item_struct *)defex_packed_rules; |
| ptr = file_path + 1; |
| do { |
| next_separator = strchr(ptr, '/'); |
| if (!next_separator) |
| l = strlen(ptr); |
| else |
| l = next_separator - ptr; |
| if (!l) |
| return 0; |
| cur_item = lookup_dir(base, ptr, l); |
| if (!cur_item) |
| break; |
| if (cur_item->feature_type & attribute) { |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| if (defex_integrity_default(file_path) |
| && defex_check_integrity(f, cur_item->integrity)) |
| return DEFEX_INTEGRITY_FAIL; |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| return 1; |
| } |
| base = cur_item; |
| ptr += l; |
| if (next_separator) |
| ptr++; |
| } while(*ptr); |
| return 0; |
| } |
| #endif /* DEFEX_USE_PACKED_RULES */ |
| |
| int rules_lookup(const struct path *dpath, int attribute, struct file *f) |
| { |
| int ret = 0; |
| static const char system_root_txt[] = "/system_root"; |
| #if (defined(DEFEX_SAFEPLACE_ENABLE) || defined(DEFEX_PED_ENABLE)) |
| char *target_file, *buff; |
| #ifndef DEFEX_USE_PACKED_RULES |
| int i, count, end; |
| const struct static_rule *current_rule; |
| #endif |
| buff = kzalloc(PATH_MAX, GFP_ATOMIC); |
| if (!buff) |
| return ret; |
| target_file = d_path(dpath, buff, PATH_MAX); |
| if (IS_ERR(target_file)) { |
| kfree(buff); |
| return ret; |
| } |
| if (check_system_mount() && |
| !strncmp(target_file, system_root_txt, sizeof(system_root_txt) - 1)) |
| target_file += (sizeof(system_root_txt) - 1); |
| |
| #ifdef DEFEX_USE_PACKED_RULES |
| ret = lookup_tree(target_file, attribute, f); |
| #else |
| for (i = 0; i < static_rule_count; i++) { |
| current_rule = &defex_static_rules[i]; |
| if (current_rule->feature_type == attribute) { |
| end = strnlen(current_rule->rule, STATIC_RULES_MAX_STR); |
| if (current_rule->rule[end - 1] == '/') { |
| count = end; |
| } else { |
| count = strnlen(target_file, STATIC_RULES_MAX_STR); |
| if (end > count) count = end; |
| } |
| if (!strncmp(current_rule->rule, target_file, count)) { |
| ret = 1; |
| break; |
| } |
| } |
| } |
| #endif /* DEFEX_USE_PACKED_RULES */ |
| kfree(buff); |
| #endif |
| return ret; |
| } |
| |
| int defex_init_sysfs(void) |
| { |
| defex_kset = kset_create_and_add("defex", NULL, NULL); |
| if (!defex_kset) |
| return -ENOMEM; |
| |
| #if defined(DEFEX_DEBUG_ENABLE) && defined(DEFEX_SYSFS_ENABLE) |
| if (defex_create_debug(defex_kset) != DEFEX_OK) |
| goto kset_error; |
| #endif /* DEFEX_DEBUG_ENABLE && DEFEX_SYSFS_ENABLE */ |
| |
| #ifdef DEFEX_PED_ENABLE |
| global_privesc_obj = task_defex_create_privesc_obj(defex_kset); |
| if (!global_privesc_obj) |
| goto privesc_error; |
| #endif /* DEFEX_PED_ENABLE */ |
| |
| #ifdef DEFEX_SAFEPLACE_ENABLE |
| global_safeplace_obj = task_defex_create_safeplace_obj(defex_kset); |
| if (!global_safeplace_obj) |
| goto safeplace_error; |
| #endif /* DEFEX_SAFEPLACE_ENABLE */ |
| |
| parse_static_rules(defex_static_rules, STATIC_RULES_MAX_STR, static_rule_count); |
| return 0; |
| |
| #ifdef DEFEX_SAFEPLACE_ENABLE |
| safeplace_error: |
| #endif /* DEFEX_SAFEPLACE_ENABLE */ |
| |
| #ifdef DEFEX_PED_ENABLE |
| task_defex_destroy_privesc_obj(global_privesc_obj); |
| privesc_error: |
| #endif /* DEFEX_PED_ENABLE */ |
| |
| #if defined(DEFEX_DEBUG_ENABLE) && defined(DEFEX_SYSFS_ENABLE) |
| kset_error: |
| kset_unregister(defex_kset); |
| defex_kset = NULL; |
| #endif /* DEFEX_DEBUG_ENABLE && DEFEX_SYSFS_ENABLE */ |
| return -ENOMEM; |
| } |