diff options
Diffstat (limited to 'syscall_filter.c')
-rw-r--r-- | syscall_filter.c | 99 |
1 files changed, 96 insertions, 3 deletions
diff --git a/syscall_filter.c b/syscall_filter.c index 2c389ae..fcdbaa8 100644 --- a/syscall_filter.c +++ b/syscall_filter.c @@ -123,6 +123,13 @@ void append_ret_kill(struct filter_block *head) append_filter_block(head, filter, ONE_INSTR); } +void append_ret_kill_process(struct filter_block *head) +{ + struct sock_filter *filter = new_instr_buf(ONE_INSTR); + set_bpf_ret_kill_process(filter); + append_filter_block(head, filter, ONE_INSTR); +} + void append_ret_trap(struct filter_block *head) { struct sock_filter *filter = new_instr_buf(ONE_INSTR); @@ -154,12 +161,47 @@ void append_allow_syscall(struct filter_block *head, int nr) append_filter_block(head, filter, len); } +void copy_parser_state(struct parser_state *src, struct parser_state *dest) +{ + const char *filename = strdup(src->filename); + if (!filename) + pdie("strdup(src->filename) failed"); + + dest->line_number = src->line_number; + dest->filename = filename; +} + +/* + * Inserts the current state into the array of previous syscall states at the + * index |ind| if it is a newly encountered syscall. Returns true if it is a + * newly encountered syscall and false if it is a duplicate. + */ +bool insert_and_check_duplicate_syscall(struct parser_state **previous_syscalls, + struct parser_state *state, size_t ind) +{ + if (ind >= get_num_syscalls()) { + die("syscall index %zu out of range: %zu total syscalls", ind, + get_num_syscalls()); + } + struct parser_state *prev_state_ptr = previous_syscalls[ind]; + if (prev_state_ptr == NULL) { + previous_syscalls[ind] = calloc(1, sizeof(struct parser_state)); + if (!previous_syscalls[ind]) + die("could not allocate parser_state buffer"); + copy_parser_state(state, previous_syscalls[ind]); + return true; + } + return false; +} + void allow_logging_syscalls(struct filter_block *head) { unsigned int i; + for (i = 0; i < log_syscalls_len; i++) { warn("allowing syscall: %s", log_syscalls[i]); - append_allow_syscall(head, lookup_syscall(log_syscalls[i])); + append_allow_syscall(head, + lookup_syscall(log_syscalls[i], NULL)); } } @@ -303,6 +345,9 @@ int compile_errno(struct parser_state *state, struct filter_block *head, case ACTION_RET_KILL: append_ret_kill(head); break; + case ACTION_RET_KILL_PROCESS: + append_ret_kill_process(head); + break; case ACTION_RET_TRAP: append_ret_trap(head); break; @@ -447,6 +492,9 @@ struct filter_block *compile_policy_line(struct parser_state *state, int nr, case ACTION_RET_KILL: append_ret_kill(head); break; + case ACTION_RET_KILL_PROCESS: + append_ret_kill_process(head); + break; case ACTION_RET_TRAP: append_ret_trap(head); break; @@ -552,6 +600,10 @@ static ssize_t getmultiline(char **lineptr, size_t *n, FILE *stream) /* Merge the lines. */ *n = ret + next_ret + 2; line = realloc(line, *n); + if (!line) { + free(next_line); + return -1; + } line[ret] = ' '; memcpy(&line[ret + 1], next_line, next_ret + 1); free(next_line); @@ -563,6 +615,7 @@ int compile_file(const char *filename, FILE *policy_file, struct filter_block *head, struct filter_block **arg_blocks, struct bpf_labels *labels, const struct filter_options *filteropts, + struct parser_state **previous_syscalls, unsigned int include_level) { /* clang-format off */ @@ -626,6 +679,7 @@ int compile_file(const char *filename, FILE *policy_file, } if (compile_file(filename, included_file, head, arg_blocks, labels, filteropts, + previous_syscalls, include_level + 1) == -1) { compiler_warn(&state, "'@include %s' failed", filename); @@ -657,7 +711,8 @@ int compile_file(const char *filename, FILE *policy_file, } syscall_name = strip(syscall_name); - int nr = lookup_syscall(syscall_name); + size_t ind = 0; + int nr = lookup_syscall(syscall_name, &ind); if (nr < 0) { compiler_warn(&state, "nonexistent syscall '%s'", syscall_name); @@ -680,6 +735,16 @@ int compile_file(const char *filename, FILE *policy_file, goto free_line; } + if (!insert_and_check_duplicate_syscall(previous_syscalls, + &state, ind)) { + if (!filteropts->allow_duplicate_syscalls) + ret = -1; + compiler_warn(&state, "syscall %s redefined here", + lookup_syscall_name(nr)); + compiler_warn(previous_syscalls[ind], + "previous definition here"); + } + /* * For each syscall, add either a simple ALLOW, * or an arg filter block. @@ -752,6 +817,14 @@ int compile_filter(const char *filename, FILE *initial_file, struct filter_block *head = new_filter_block(); struct filter_block *arg_blocks = NULL; + /* + * Create the data structure that will keep track of what system + * calls we have already defined if the option is true. + */ + size_t num_syscalls = get_num_syscalls(); + struct parser_state **previous_syscalls = + calloc(num_syscalls, sizeof(*previous_syscalls)); + /* Start filter by validating arch. */ struct sock_filter *valid_arch = new_instr_buf(ARCH_VALIDATION_LEN); size_t len = bpf_validate_arch(valid_arch); @@ -771,7 +844,8 @@ int compile_filter(const char *filename, FILE *initial_file, allow_logging_syscalls(head); if (compile_file(filename, initial_file, head, &arg_blocks, &labels, - filteropts, 0 /* include_level */) != 0) { + filteropts, previous_syscalls, + 0 /* include_level */) != 0) { warn("compile_filter: compile_file() failed"); ret = -1; goto free_filter; @@ -785,6 +859,9 @@ int compile_filter(const char *filename, FILE *initial_file, case ACTION_RET_KILL: append_ret_kill(head); break; + case ACTION_RET_KILL_PROCESS: + append_ret_kill_process(head); + break; case ACTION_RET_TRAP: append_ret_trap(head); break; @@ -815,6 +892,8 @@ int compile_filter(const char *filename, FILE *initial_file, struct sock_filter *final_filter = calloc(final_filter_len, sizeof(struct sock_filter)); + if (!final_filter) + die("could not allocate final BPF filter"); if (flatten_block_list(head, final_filter, 0, final_filter_len) < 0) { free(final_filter); @@ -842,6 +921,7 @@ free_filter: free_block_list(head); free_block_list(arg_blocks); free_label_strings(&labels); + free_previous_syscalls(previous_syscalls); return ret; } @@ -875,3 +955,16 @@ void free_block_list(struct filter_block *head) free(prev); } } + +void free_previous_syscalls(struct parser_state **previous_syscalls) +{ + size_t num_syscalls = get_num_syscalls(); + for (size_t i = 0; i < num_syscalls; i++) { + struct parser_state *state = previous_syscalls[i]; + if (state) { + free((char *)state->filename); + free(state); + } + } + free(previous_syscalls); +} |