kernel_samsung_a34x-permissive/security/samsung/defex_lsm/pack_rules.c
2024-04-28 15:49:01 +02:00

679 lines
17 KiB
C
Executable file

/*
* 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},
{"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 <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;
}