diff options
Diffstat (limited to 'syscall_filter.c')
-rw-r--r-- | syscall_filter.c | 120 |
1 files changed, 105 insertions, 15 deletions
diff --git a/syscall_filter.c b/syscall_filter.c index 3318052..956fe21 100644 --- a/syscall_filter.c +++ b/syscall_filter.c @@ -164,6 +164,11 @@ unsigned int success_lbl(struct bpf_labels *labels, int nr) return get_label_id(labels, lbl_str); } +int is_implicit_relative_path(const char *filename) +{ + return filename[0] != '/' && (filename[0] != '.' || filename[1] != '/'); +} + int compile_atom(struct filter_block *head, char *atom, struct bpf_labels *labels, int nr, int grp_idx) { @@ -428,9 +433,57 @@ struct filter_block *compile_policy_line(int nr, const char *policy_line, return head; } +int parse_include_statement(char *policy_line, unsigned int include_level, + const char **ret_filename) +{ + if (strncmp("@include", policy_line, strlen("@include")) != 0) { + warn("invalid statement '%s'", policy_line); + return -1; + } + + if (policy_line[strlen("@include")] != ' ') { + warn("invalid include statement '%s'", policy_line); + return -1; + } + + /* + * Disallow nested includes: only the initial policy file can have + * @include statements. + * Nested includes are not currently necessary and make the policy + * harder to understand. + */ + if (include_level > 0) { + warn("@include statement nested too deep"); + return -1; + } + + char *statement = policy_line; + /* Discard "@include" token. */ + (void)strsep(&statement, " "); + + /* + * compile_filter() below receives a FILE*, so it's not trivial to open + * included files relative to the initial policy filename. + * To avoid mistakes, force the included file path to be absolute + * (start with '/'), or to explicitly load the file relative to CWD by + * using './'. + */ + const char *filename = statement; + if (is_implicit_relative_path(filename)) { + warn("compile_file: implicit relative path '%s' not supported, " + "use './%s'", + filename, filename); + return -1; + } + + *ret_filename = filename; + return 0; +} + int compile_file(FILE *policy_file, struct filter_block *head, struct filter_block **arg_blocks, struct bpf_labels *labels, - int use_ret_trap, int allow_logging) + int use_ret_trap, int allow_logging, + unsigned int include_level) { /* * Loop through all the lines in the policy file. @@ -442,27 +495,62 @@ int compile_file(FILE *policy_file, struct filter_block *head, */ char *line = NULL; size_t len = 0; + int ret = 0; + while (getline(&line, &len, policy_file) != -1) { char *policy_line = line; - char *syscall_name = strsep(&policy_line, ":"); - int nr = -1; - - syscall_name = strip(syscall_name); + policy_line = strip(policy_line); /* Allow comments and empty lines. */ - if (*syscall_name == '#' || *syscall_name == '\0') { + if (*policy_line == '#' || *policy_line == '\0') { /* Reuse |line| in the next getline() call. */ continue; } + /* Allow @include statements. */ + if (*policy_line == '@') { + const char *filename = NULL; + if (parse_include_statement(policy_line, include_level, + &filename) != 0) { + warn("compile_file: failed to parse include " + "statement"); + ret = -1; + goto free_line; + } + + FILE *included_file = fopen(filename, "re"); + if (included_file == NULL) { + pwarn("compile_file: fopen('%s') failed", + filename); + ret = -1; + goto free_line; + } + if (compile_file(included_file, head, arg_blocks, + labels, use_ret_trap, allow_logging, + ++include_level) == -1) { + warn("compile_file: '@include %s' failed", + filename); + fclose(included_file); + ret = -1; + goto free_line; + } + fclose(included_file); + continue; + } + + /* + * If it's not a comment, or an empty line, or an @include + * statement, treat |policy_line| as a regular policy line. + */ + char *syscall_name = strsep(&policy_line, ":"); policy_line = strip(policy_line); if (*policy_line == '\0') { warn("compile_file: empty policy line"); - free(line); - return -1; + ret = -1; + goto free_line; } - nr = lookup_syscall(syscall_name); + int nr = lookup_syscall(syscall_name); if (nr < 0) { warn("compile_file: nonexistent syscall '%s'", syscall_name); @@ -481,8 +569,8 @@ int compile_file(FILE *policy_file, struct filter_block *head, /* Reuse |line| in the next getline() call. */ continue; } - free(line); - return -1; + ret = -1; + goto free_line; } /* @@ -511,8 +599,8 @@ int compile_file(FILE *policy_file, struct filter_block *head, if (*arg_blocks) { free_block_list(*arg_blocks); } - free(line); - return -1; + ret = -1; + goto free_line; } if (*arg_blocks) { @@ -523,8 +611,10 @@ int compile_file(FILE *policy_file, struct filter_block *head, } /* Reuse |line| in the next getline() call. */ } + +free_line: free(line); - return 0; + return ret; } int compile_filter(FILE *initial_file, struct sock_fprog *prog, @@ -556,7 +646,7 @@ int compile_filter(FILE *initial_file, struct sock_fprog *prog, allow_logging_syscalls(head); if (compile_file(initial_file, head, &arg_blocks, &labels, use_ret_trap, - allow_logging) != 0) { + allow_logging, 0 /* include_level */) != 0) { warn("compile_filter: compile_file() failed"); free_block_list(head); free_block_list(arg_blocks); |