| /* |
| * 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 <limits.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "include/defex_rules.h" |
| |
| #define SAFE_STRCOPY(dst, src) do { strncpy(dst, src, sizeof(dst)); dst[sizeof(dst) - 1] = 0; } while(0) |
| |
| const struct feature_match_entry feature_match[] = { |
| {"feature_safeplace_path", feature_safeplace_path}, |
| {"feature_ped_exception", feature_ped_exception}, |
| {"feature_immutable_path_open", feature_immutable_path_open}, |
| {"feature_immutable_path_write", feature_immutable_path_write}, |
| {"feature_immutable_src_exception", feature_immutable_src_exception}, |
| {"feature_umhbin_path", feature_umhbin_path}, |
| }; |
| |
| const int feature_match_size = sizeof(feature_match) / sizeof(feature_match[0]); |
| |
| struct file_list_item { |
| char file_name[PATH_MAX]; |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| char integrity[INTEGRITY_LENGTH * 2 + 1]; |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| int is_recovery; |
| }; |
| |
| struct rule_item_struct *defex_packed_rules; |
| int packfiles_count, packfiles_size; |
| |
| struct file_list_item *file_list = NULL; |
| int file_list_count = 0; |
| |
| #ifndef DEFEX_DEBUG_ENABLE |
| int debug_ifdef_is_active = 0; |
| void process_debug_ifdef(const char *src_str); |
| #endif |
| |
| /* Suplementary functions for packing rules */ |
| struct rule_item_struct *create_file_item(const char *name, int l); |
| struct rule_item_struct *add_file_item(struct rule_item_struct *base, const char *name, int l); |
| struct rule_item_struct *lookup_dir(struct rule_item_struct *base, const char *name, int l, int for_recovery); |
| struct rule_item_struct *add_file_path(const char *file_path, int for_recovery); |
| struct rule_item_struct *addline2tree(char *src_line, enum feature_types feature); |
| char *extract_rule_text(const char *src_line); |
| int lookup_tree(const char *file_path, int attribute, int for_recovery); |
| int store_tree(FILE *f, FILE *f_bin); |
| |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| /* Transfer string to hex */ |
| unsigned char ascii_to_hex(unsigned char input); |
| int string_to_hex(unsigned char *input, size_t inputLen, unsigned char *output); |
| char null_integrity[INTEGRITY_LENGTH * 2 + 1]; |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| |
| /* Suplementary functions for reducing rules */ |
| int remove_substr(char *str, const char *part); |
| void trim_cr_lf(char *str); |
| char* remove_redundant_chars(char *str); |
| int load_file_list(const char *name); |
| int lookup_file_list(const char *rule, int for_recovery); |
| |
| /* Main processing functions */ |
| int reduce_rules(const char *source_rules_file, const char *reduced_rules_file, const char *list_file); |
| int pack_rules(const char *source_rules_file, const char *packed_rules_file, const char *packed_rules_binfile); |
| |
| |
| struct rule_item_struct *create_file_item(const char *name, int l) |
| { |
| struct rule_item_struct *item; |
| unsigned int offset; |
| |
| if (!name) |
| l = 0; |
| offset = packfiles_size; |
| packfiles_size += (sizeof(struct rule_item_struct) + l); |
| packfiles_count++; |
| item = GET_ITEM_PTR(offset); |
| item->next_file = 0; |
| item->next_level = 0; |
| item->feature_type = 0; |
| item->size = l; |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| memset(item->integrity, 0, INTEGRITY_LENGTH); |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| if (l) |
| memcpy(item->name, name, l); |
| return item; |
| } |
| |
| struct rule_item_struct *add_file_item(struct rule_item_struct *base, const char *name, int l) |
| { |
| struct rule_item_struct *item, *new_item = NULL; |
| |
| if (!base) |
| return new_item; |
| |
| new_item = create_file_item(name, l); |
| if (!base->next_level) { |
| base->next_level = GET_ITEM_OFFSET(new_item); |
| } else { |
| item = GET_ITEM_PTR(base->next_level); |
| while(item->next_file) { |
| item = GET_ITEM_PTR(item->next_file); |
| } |
| item->next_file = GET_ITEM_OFFSET(new_item); |
| } |
| return new_item; |
| } |
| |
| struct rule_item_struct *lookup_dir(struct rule_item_struct *base, const char *name, int l, int for_recovery) |
| { |
| 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->feature_type & feature_is_file) |
| || (!!(item->feature_type & feature_for_recovery)) == for_recovery) |
| && item->size == l |
| && !memcmp(name, item->name, l)) return item; |
| offset = item->next_file; |
| item = GET_ITEM_PTR(offset); |
| } while(offset); |
| return NULL; |
| } |
| |
| struct rule_item_struct *add_file_path(const char *file_path, int for_recovery) |
| { |
| const char *ptr, *next_separator; |
| struct rule_item_struct *base, *cur_item = NULL; |
| int l; |
| |
| if (!file_path || *file_path != '/') |
| return NULL; |
| if (!defex_packed_rules) { |
| packfiles_count = 0; |
| packfiles_size = 0; |
| defex_packed_rules = calloc(sizeof(struct rule_item_struct), 100 * 1024); |
| if (!defex_packed_rules) { |
| printf("WARNING: Can not create the new item!\n"); |
| exit(-1); |
| } |
| create_file_item("DEFEX_RULES_FILE", 16); |
| } |
| base = 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 NULL; /* two slashes in sequence */ |
| cur_item = lookup_dir(base, ptr, l, for_recovery); |
| if (!cur_item) { |
| cur_item = add_file_item(base, ptr, l); |
| /* slash wasn't found, it's a file */ |
| if (!next_separator) { |
| cur_item->feature_type |= feature_is_file; |
| if (for_recovery) |
| cur_item->feature_type |= feature_for_recovery; |
| } |
| } |
| base = cur_item; |
| ptr += l; |
| if (next_separator) |
| ptr++; |
| } while(*ptr); |
| return cur_item; |
| } |
| |
| int lookup_tree(const char *file_path, int attribute, int for_recovery) |
| { |
| const char *ptr, *next_separator; |
| struct rule_item_struct *base, *cur_item = NULL; |
| int l; |
| |
| if (!file_path || *file_path != '/' || !defex_packed_rules) |
| return 0; |
| base = 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, for_recovery); |
| if (!cur_item) |
| break; |
| if (cur_item->feature_type & attribute) |
| return 1; |
| base = cur_item; |
| ptr += l; |
| if (next_separator) |
| ptr++; |
| } while(*ptr); |
| return 0; |
| } |
| |
| char *extract_rule_text(const char *src_line) |
| { |
| char *start_ptr, *end_ptr; |
| |
| start_ptr = strchr(src_line, '\"'); |
| if (start_ptr) { |
| start_ptr++; |
| end_ptr = strchr(start_ptr, '\"'); |
| if (end_ptr) { |
| *end_ptr = 0; |
| return start_ptr; |
| } |
| } |
| return NULL; |
| } |
| |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| unsigned char ascii_to_hex(unsigned char input) |
| { |
| if (input >= 0x30 && input <=0x39) |
| return input - 0x30; |
| else if (input >= 0x41 && input <= 0x46) |
| return input - 0x37; |
| else if (input >= 0x61 && input <= 0x66) |
| return input - 0x57; |
| else |
| return 0xFF; |
| } |
| |
| int string_to_hex(unsigned char *input, size_t inputLen, unsigned char *output) |
| { |
| unsigned char convert1, convert2; |
| size_t i; |
| |
| if (input == NULL || output == NULL) |
| return 0; |
| |
| // Check input is a paired value. |
| if (inputLen % 2 != 0) |
| return 0; |
| |
| // Convert ascii code to hexa. |
| for (i = 0; i < inputLen / 2; i++) { |
| convert1 = ascii_to_hex(input[2*i]); |
| convert2 = ascii_to_hex(input[2*i+1]); |
| |
| if (convert1 == 0xFF || convert2 == 0xFF) |
| return 0; |
| |
| output[i] = (char)((convert1 << 4) | convert2); |
| } |
| |
| return 1; |
| } |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| |
| struct rule_item_struct *addline2tree(char *src_line, enum feature_types feature) |
| { |
| struct rule_item_struct *item = NULL; |
| char *str; |
| |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| unsigned char *integrity, *n_sign = NULL, *r_sign = NULL; |
| int value; |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| |
| str = extract_rule_text(src_line); |
| if (!str) |
| return NULL; |
| |
| #ifndef DEFEX_INTEGRITY_ENABLE |
| item = add_file_path(str, 0); |
| if (item) |
| item->feature_type |= feature; |
| #else |
| integrity = (unsigned char *)extract_rule_text(str + strnlen(str, PATH_MAX) + 1); |
| if (integrity) { |
| n_sign = (unsigned char *)strchr((const char *)integrity, 'N'); |
| r_sign = (unsigned char *)strchr((const char *)integrity, 'R'); |
| } |
| if (!(n_sign == NULL && r_sign != NULL)) { |
| item = add_file_path(str, 0); |
| if (item) { |
| item->feature_type |= feature; |
| if (n_sign) { |
| value = string_to_hex(n_sign + 1, INTEGRITY_LENGTH * 2, item->integrity); |
| if (!value) |
| return NULL; |
| } |
| |
| } |
| } |
| if (r_sign != NULL) { |
| item = add_file_path(str, 1); |
| if (item) { |
| item->feature_type |= feature; |
| value = string_to_hex(r_sign + 1, INTEGRITY_LENGTH * 2, item->integrity); |
| if (!value) |
| return NULL; |
| |
| } |
| } |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| return item; |
| } |
| |
| int store_tree(FILE *f, FILE *f_bin) |
| { |
| unsigned char *ptr = (unsigned char *)defex_packed_rules; |
| static char work_str[4096]; |
| int i, offset = 0, index = 0; |
| |
| if (packfiles_size) |
| defex_packed_rules->data_size = packfiles_size; |
| |
| work_str[0] = 0; |
| fprintf(f, "#ifndef DEFEX_RAMDISK_ENABLE\n\n"); |
| fprintf(f, "const unsigned char defex_packed_rules[] = {\n"); |
| for (i = 0; i < packfiles_size; i++) { |
| if (index) |
| offset += snprintf(work_str + offset, sizeof(work_str) - offset, ", "); |
| offset += snprintf(work_str + offset, sizeof(work_str) - offset, "0x%02x", ptr[i]); |
| index++; |
| if (index == 16) { |
| fprintf(f, "\t%s,\n", work_str); |
| index = 0; |
| offset = 0; |
| } |
| } |
| if (index) |
| fprintf(f, "\t%s\n", work_str); |
| fprintf(f, "};\n"); |
| fprintf(f, "\n#endif /* DEFEX_RAMDISK_ENABLE */\n\n"); |
| fprintf(f, "#define DEFEX_RULES_ARRAY_SIZE\t\t%d\n", packfiles_size); |
| if (f_bin) |
| fwrite(defex_packed_rules, 1, packfiles_size, f_bin); |
| return 0; |
| } |
| |
| int remove_substr(char *str, const char *part) |
| { |
| int l, part_l, found = 0; |
| char *ptr; |
| |
| l = strnlen(str, PATH_MAX - 1); |
| ptr = strstr(str, part); |
| if (ptr) { |
| part_l = strnlen(part, 32) - 1; |
| memmove(ptr, ptr + part_l, l - (ptr - str) - part_l + 1); |
| found = 1; |
| } |
| return found; |
| } |
| |
| void trim_cr_lf(char *str) |
| { |
| char *ptr; |
| /* remove CR or LF at the end */ |
| ptr = strchr(str, '\r'); |
| if (ptr) |
| *ptr = 0; |
| ptr = strchr(str, '\n'); |
| if (ptr) |
| *ptr = 0; |
| } |
| |
| char* remove_redundant_chars(char *str) |
| { |
| int l; |
| |
| /* skip hash values in the begin */ |
| str += 65; |
| trim_cr_lf(str); |
| l = strnlen(str, PATH_MAX - 1); |
| /* remove starting dot or space */ |
| while(l && (*str == '.' || *str == ' ')) |
| str++; |
| return str; |
| } |
| |
| int load_file_list(const char *name) |
| { |
| int found; |
| char *str; |
| FILE *lst_file = NULL; |
| static char work_str[PATH_MAX*2]; |
| |
| lst_file = fopen(name, "r"); |
| if (!lst_file) |
| return -1; |
| |
| while(!feof(lst_file)) { |
| if (!fgets(work_str, sizeof(work_str), lst_file)) |
| break; |
| str = remove_redundant_chars(work_str); |
| if (*str == '/' && |
| (!strncmp(str, "/root/", 6) || |
| !strncmp(str, "/product/", 9) || |
| !strncmp(str, "/recovery/", 10) || |
| !strncmp(str, "/system/", 8) || |
| !strncmp(str, "/tmp/", 5) || |
| !strncmp(str, "/vendor/", 8) || |
| #if defined(DEFEX_FACTORY_ENABLE) |
| !strncmp(str, "/data/", 6) || |
| #endif |
| !strncmp(str, "/apex/", 6))) { |
| remove_substr(str, "/root/"); |
| found = remove_substr(str, "/recovery/"); |
| file_list_count++; |
| file_list = realloc(file_list, sizeof(struct file_list_item) * file_list_count); |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| strncpy(file_list[file_list_count - 1].integrity, work_str, INTEGRITY_LENGTH * 2); |
| file_list[file_list_count - 1].integrity[INTEGRITY_LENGTH * 2] = 0; |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| SAFE_STRCOPY(file_list[file_list_count - 1].file_name, str); |
| file_list[file_list_count - 1].is_recovery = found; |
| } |
| } |
| fclose(lst_file); |
| return 0; |
| } |
| |
| int lookup_file_list(const char *rule, int for_recovery) |
| { |
| int i; |
| |
| for (i = 0; i < file_list_count; i++) { |
| if (file_list[i].is_recovery == for_recovery |
| && !strncmp(file_list[i].file_name, rule, strnlen(rule, PATH_MAX)) |
| && !strncmp(file_list[i].file_name, rule, strnlen(file_list[i].file_name, PATH_MAX))) |
| return i+1; |
| } |
| return 0; |
| } |
| |
| #ifndef DEFEX_DEBUG_ENABLE |
| void process_debug_ifdef(const char *src_str) |
| { |
| char *ptr; |
| ptr = strstr(src_str, "#ifdef DEFEX_DEBUG_ENABLE"); |
| if (ptr) { |
| while(ptr > src_str) { |
| ptr--; |
| if (*ptr != ' ' && *ptr != '\t') |
| return; |
| } |
| debug_ifdef_is_active = 1; |
| return; |
| } |
| ptr = strstr(src_str, "#endif"); |
| if (ptr && debug_ifdef_is_active) { |
| while(ptr > src_str) { |
| ptr--; |
| if (*ptr != ' ' && *ptr != '\t') |
| return; |
| } |
| debug_ifdef_is_active = 0; |
| return; |
| } |
| } |
| #endif |
| |
| static int str_to_feature(const char *str) |
| { |
| int i; |
| |
| for (i = 0; i < feature_match_size; i++) { |
| if (strstr(str, feature_match[i].feature_name)) { |
| return feature_match[i].feature_num; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int reduce_rules(const char *source_rules_file, const char *reduced_rules_file, const char *list_file) |
| { |
| int ret_val = -1; |
| int found_normal = 0, found_recovery = 0; |
| char *rule; |
| static char work_str[PATH_MAX*2], tmp_str[PATH_MAX*2]; |
| FILE *src_file = NULL, *dst_file = NULL; |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| char *line_end, *integrity_normal; |
| char *integrity_recovery; |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| |
| src_file = fopen(source_rules_file, "r"); |
| if (!src_file) |
| return -1; |
| dst_file = fopen(reduced_rules_file, "wt"); |
| if (!dst_file) |
| goto do_close2; |
| |
| if (load_file_list(list_file) != 0) |
| goto do_close1; |
| |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| memset(null_integrity, '0', sizeof(null_integrity) - 1); |
| null_integrity[sizeof(null_integrity) - 1] = 0; |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| |
| while(!feof(src_file)) { |
| if (!fgets(work_str, sizeof(work_str), src_file)) |
| break; |
| |
| if (str_to_feature(work_str)) { |
| trim_cr_lf(work_str); |
| SAFE_STRCOPY(tmp_str, work_str); |
| rule = extract_rule_text(tmp_str); |
| found_normal = lookup_file_list(rule, 0); |
| found_recovery = lookup_file_list(rule, 1); |
| if (rule && !found_normal && !found_recovery && !strstr(work_str, "/* DEFAULT */")) { |
| printf("removed rule: %s\n", rule); |
| continue; |
| } |
| #ifdef DEFEX_INTEGRITY_ENABLE |
| integrity_normal = null_integrity; |
| integrity_recovery = null_integrity; |
| if (found_normal) |
| integrity_normal = file_list[found_normal - 1].integrity; |
| if (found_recovery) |
| integrity_recovery = file_list[found_recovery - 1].integrity; |
| |
| line_end = strstr(work_str, "},"); |
| if (line_end) { |
| *line_end = 0; |
| line_end += 2; |
| } |
| |
| /* Add hash vale after each file path */ |
| if (found_normal || (!found_normal && !found_recovery)) |
| printf("remained rule: %s, %s %s\n", rule, integrity_normal, (line_end != NULL)?line_end:""); |
| if (found_recovery) |
| printf("remained rule: %s, %s %s\n", rule, integrity_recovery, (line_end != NULL)?line_end:""); |
| |
| fprintf(dst_file, "%s,\"", work_str); |
| if (found_normal) |
| fprintf(dst_file, "N%s", integrity_normal); |
| if (found_recovery) |
| fprintf(dst_file, "R%s", integrity_recovery); |
| |
| fprintf(dst_file, "\"}, %s\n", (line_end != NULL)?line_end:""); |
| |
| #else |
| printf("remained rule: %s\n", work_str); |
| fputs(work_str, dst_file); |
| fputs("\n", dst_file); |
| #endif /* DEFEX_INTEGRITY_ENABLE */ |
| } else |
| fputs(work_str, dst_file); |
| } |
| ret_val = 0; |
| do_close1: |
| fclose(dst_file); |
| do_close2: |
| fclose(src_file); |
| return ret_val; |
| } |
| |
| int pack_rules(const char *source_rules_file, const char *packed_rules_file, const char *packed_rules_binfile) |
| { |
| int ret_val = -1; |
| int feature; |
| FILE *src_file = NULL, *dst_file = NULL, *dst_binfile = NULL; |
| static char work_str[PATH_MAX*2]; |
| |
| src_file = fopen(source_rules_file, "r"); |
| if (!src_file) |
| return -1; |
| dst_file = fopen(packed_rules_file, "wt"); |
| if (!dst_file) |
| goto do_close2; |
| if (packed_rules_binfile) { |
| dst_binfile = fopen(packed_rules_binfile, "wt"); |
| if (!dst_binfile) |
| goto do_close3; |
| } |
| |
| while(!feof(src_file)) { |
| if (!fgets(work_str, sizeof(work_str), src_file)) |
| break; |
| #ifndef DEFEX_DEBUG_ENABLE |
| process_debug_ifdef(work_str); |
| if (!debug_ifdef_is_active) { |
| #endif |
| feature = str_to_feature(work_str); |
| if (feature) { |
| addline2tree(work_str, feature); |
| continue; |
| } |
| #ifndef DEFEX_DEBUG_ENABLE |
| } |
| #endif |
| } |
| store_tree(dst_file, dst_binfile); |
| if (!packfiles_count) |
| printf("WARNING: Defex packed rules tree is empty!\n"); |
| ret_val = 0; |
| if (dst_binfile) |
| fclose(dst_binfile); |
| do_close3: |
| fclose(dst_file); |
| do_close2: |
| fclose(src_file); |
| return ret_val; |
| } |
| |
| |
| int main(int argc, char **argv) |
| { |
| static char param[4][PATH_MAX]; |
| char *src_file = NULL, *packed_file = NULL, *packed_bin_file = NULL; |
| char *reduced_file = NULL, *list_file = NULL; |
| int i; |
| |
| if (argc < 4 || argc > 5) |
| goto show_help; |
| |
| for(i = 0; i < (argc - 2); i++) { |
| SAFE_STRCOPY(param[i], argv[i + 2]); |
| switch(i) { |
| case 0: |
| src_file = param[i]; |
| break; |
| case 1: |
| packed_file = reduced_file = param[i]; |
| break; |
| case 2: |
| packed_bin_file = list_file = param[i]; |
| break; |
| } |
| } |
| |
| if (!strncmp(argv[1], "-p", 2)) { |
| if (pack_rules(src_file, packed_file, packed_bin_file) != 0) |
| goto show_help; |
| return 0; |
| |
| } else if (!strncmp(argv[1], "-r", 2) && list_file) { |
| if (reduce_rules(src_file, reduced_file, list_file) != 0) |
| goto show_help; |
| return 0; |
| } |
| |
| show_help: |
| printf("Defex rules processing utility.\nUSAGE:\n%s <CMD> <PARAMS>\n" |
| "Commands:\n" |
| " -p - Pack rules file to the tree. Params: <SOURCE_FILE> <PACKED_FILE> [PACKED_BIN_FILE]\n" |
| " -r - Reduce rules file (remove unexistent files). Params: <SOURCE_FILE> <REDUCED_FILE> <FILE_LIST>\n", |
| argv[0]); |
| return -1; |
| } |