blob: 8e1f0a38447501352ab6a2965bcfda0f926038a8 [file] [log] [blame]
/*
* 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;
}