You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
574 lines
14 KiB
574 lines
14 KiB
/*
|
|
* 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/limits.h>
|
|
#include <linux/types.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)
|
|
|
|
struct file_list_item {
|
|
char file_name[PATH_MAX];
|
|
#ifdef DEFEX_INTEGRITY_ENABLE
|
|
char integrity[INTEGRITY_LENGTH * 2 + 1];
|
|
#endif /* DEFEX_INTEGRITY_ENABLE */
|
|
unsigned 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);
|
|
struct rule_item_struct *add_file_path(const char *file_path);
|
|
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 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);
|
|
#endif /* DEFEX_INTEGRITY_ENABLE */
|
|
|
|
/* Suplementary functions for reducing rules */
|
|
int remove_substr(char *str, const char *part);
|
|
char* remove_redundant_chars(char *str);
|
|
int load_file_list(const char *name);
|
|
int lookup_file_list(const char *rule);
|
|
|
|
/* 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
struct rule_item_struct *add_file_path(const char *file_path)
|
|
{
|
|
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(4, 1024 * 1024);
|
|
if (!defex_packed_rules) {
|
|
printf("WARNING: Can not create the new item!\n");
|
|
exit(-1);
|
|
}
|
|
create_file_item("HEAD", 4);
|
|
}
|
|
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);
|
|
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;
|
|
}
|
|
base = cur_item;
|
|
ptr += l;
|
|
if (next_separator)
|
|
ptr++;
|
|
} while(*ptr);
|
|
return cur_item;
|
|
}
|
|
|
|
int lookup_tree(const char *file_path, int attribute)
|
|
{
|
|
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);
|
|
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)
|
|
{
|
|
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] = (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;
|
|
int value;
|
|
#endif /* DEFEX_INTEGRITY_ENABLE */
|
|
|
|
str = extract_rule_text(src_line);
|
|
if (str == NULL)
|
|
return NULL;
|
|
|
|
#ifdef DEFEX_INTEGRITY_ENABLE
|
|
integrity = (unsigned char *)extract_rule_text(str + strnlen(str, PATH_MAX) +1);
|
|
#endif /* DEFEX_INTEGRITY_ENABLE */
|
|
|
|
if (str) {
|
|
item = add_file_path(str);
|
|
if (item) {
|
|
item->feature_type |= feature;
|
|
|
|
#ifdef DEFEX_INTEGRITY_ENABLE
|
|
if (integrity) {
|
|
value = string_to_hex(integrity, 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;
|
|
|
|
work_str[0] = 0;
|
|
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");
|
|
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;
|
|
}
|
|
|
|
char* remove_redundant_chars(char *str)
|
|
{
|
|
int l;
|
|
char *ptr;
|
|
|
|
/* skip hash values in the begin */
|
|
str += 65;
|
|
|
|
/* remove CR or LF at the end */
|
|
ptr = strchr(str, '\r');
|
|
if (ptr)
|
|
*ptr = 0;
|
|
ptr = strchr(str, '\n');
|
|
if (ptr)
|
|
*ptr = 0;
|
|
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, "/recovery/", 10) ||
|
|
!strncmp(str, "/system/", 8) ||
|
|
!strncmp(str, "/tmp/", 5) ||
|
|
!strncmp(str, "/vendor/", 8) ||
|
|
!strncmp(str, "/data/", 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 i;
|
|
|
|
for (i = 0; i < file_list_count; i++) {
|
|
if (!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
|
|
|
|
int reduce_rules(const char *source_rules_file, const char *reduced_rules_file, const char *list_file)
|
|
{
|
|
int exist = 0, ret_val = -1;
|
|
char *ptr, *rule;
|
|
static char work_str[PATH_MAX*2], tmp_str[PATH_MAX*2];
|
|
FILE *src_file = NULL, *dst_file = NULL;
|
|
|
|
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;
|
|
|
|
while(!feof(src_file)) {
|
|
if (!fgets(work_str, sizeof(work_str), src_file))
|
|
break;
|
|
ptr = strstr(work_str, "feature_safeplace_path");
|
|
if (!ptr)
|
|
ptr = strstr(work_str, "feature_ped_exception");
|
|
|
|
if (ptr) {
|
|
SAFE_STRCOPY(tmp_str, work_str);
|
|
rule = extract_rule_text(tmp_str);
|
|
exist = lookup_file_list(rule);
|
|
if (rule && !exist && !strstr(work_str, "/* DEFAULT */")) {
|
|
printf("- removed rule: %s\n", rule);
|
|
continue;
|
|
}
|
|
}
|
|
#ifdef DEFEX_INTEGRITY_ENABLE
|
|
if (exist) {
|
|
/* Add hash vale after each file path */
|
|
printf("remained rule: %s, %s\n", rule, file_list[exist-1].integrity);
|
|
work_str[strnlen(work_str, PATH_MAX)-3]=0;
|
|
fputs(work_str, dst_file);
|
|
fputs(",\"", dst_file);
|
|
fputs(file_list[exist-1].integrity, dst_file);
|
|
fputs("\"},\n", dst_file);
|
|
exist = 0;
|
|
}
|
|
#else
|
|
fputs(work_str, dst_file);
|
|
#endif /* DEFEX_INTEGRITY_ENABLE */
|
|
}
|
|
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;
|
|
char *ptr;
|
|
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
|
|
ptr = strstr(work_str, "feature_safeplace_path");
|
|
if (ptr) {
|
|
addline2tree(work_str, feature_safeplace_path);
|
|
continue;
|
|
}
|
|
|
|
ptr = strstr(work_str, "feature_ped_exception");
|
|
if (ptr) {
|
|
addline2tree(work_str, feature_ped_exception);
|
|
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 param1[PATH_MAX], param2[PATH_MAX], param3[PATH_MAX];
|
|
|
|
if (argc >= 4 && argc <= 5) {
|
|
SAFE_STRCOPY(param1, argv[2]);
|
|
SAFE_STRCOPY(param2, argv[3]);
|
|
if (argc == 5) {
|
|
SAFE_STRCOPY(param3, argv[4]);
|
|
}
|
|
if (argc == 5 && !strncmp(argv[1], "-r", 2)) {
|
|
if (reduce_rules(param1, param2, param3) != 0)
|
|
goto show_help;
|
|
}
|
|
|
|
if (argc == 4 && !strncmp(argv[1], "-p", 2)) {
|
|
if (pack_rules(param1, param2, NULL) != 0)
|
|
goto show_help;
|
|
}
|
|
|
|
if (argc == 5 && !strncmp(argv[1], "-p", 2)) {
|
|
if (pack_rules(param1, param2, param3) != 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>\n"
|
|
" -r - Reduce rules file (remove unexistent files). Params: <SOURCE_FILE> <REDUCED_FILE> <FILE_LIST>\n",
|
|
argv[0]);
|
|
return -1;
|
|
}
|
|
|