/* * 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/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}, {"feature_integrity_check", feature_integrity_check}, }; 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, defex_packed_rules); 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, defex_packed_rules); while(item->next_file) { item = GET_ITEM_PTR(item->next_file, defex_packed_rules); } 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, defex_packed_rules); 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, defex_packed_rules); } 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; struct file_list_item *file_list_new; 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) || !strncmp(str, "/system_ext/", 12))) { remove_substr(str, "/root/"); found = remove_substr(str, "/recovery/"); file_list_count++; file_list_new = realloc(file_list, sizeof(struct file_list_item) * file_list_count); if (!file_list_new) { free(file_list); printf("WARNING: Can not allocate the filelist item!\n"); exit(-1); } file_list = file_list_new; #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 (R)\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 \n" "Commands:\n" " -p - Pack rules file to the tree. Params: [PACKED_BIN_FILE]\n" " -r - Reduce rules file (remove unexistent files). Params: \n", argv[0]); return -1; }