/* * 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 #include #include #include #include #include #include #include #include #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 #include #include #include #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; }