aboutsummaryrefslogtreecommitdiff
path: root/syscall_filter.c
diff options
context:
space:
mode:
authorJorge Lucangeli Obes <jorgelo@chromium.org>2013-03-26 15:11:30 -0700
committerChromeBot <chrome-bot@google.com>2013-04-01 18:32:12 -0700
commitd96baf4ebf0a35aca42c38f25c1b20424e273e5d (patch)
treec0349676b6db4cf0b0a0cf9f6ebd12929d6dc360 /syscall_filter.c
parent6c0863036842df03a681307d2da84d2b0f7f908f (diff)
downloadminijail-d96baf4ebf0a35aca42c38f25c1b20424e273e5d.tar.gz
Add "unconditional errno" support to syscall filter policies.
BUG=chromium:224082 TEST=syscall_filter_unittest Change-Id: Ic83ecb72af8b62f297f1b6a3dc49eb3219029715 Reviewed-on: https://gerrit.chromium.org/gerrit/46568 Tested-by: Jorge Lucangeli Obes <jorgelo@chromium.org> Reviewed-by: Kees Cook <keescook@chromium.org> Commit-Queue: Jorge Lucangeli Obes <jorgelo@chromium.org>
Diffstat (limited to 'syscall_filter.c')
-rw-r--r--syscall_filter.c70
1 files changed, 42 insertions, 28 deletions
diff --git a/syscall_filter.c b/syscall_filter.c
index e10c7bc..9d4ad58 100644
--- a/syscall_filter.c
+++ b/syscall_filter.c
@@ -39,6 +39,18 @@ struct sock_filter *new_instr_buf(size_t count)
return buf;
}
+struct filter_block *new_filter_block()
+{
+ struct filter_block *block = calloc(1, sizeof(struct filter_block));
+ if (!block)
+ die("could not allocate BPF filter block");
+
+ block->instrs = NULL;
+ block->last = block->next = NULL;
+
+ return block;
+}
+
void append_filter_block(struct filter_block *head,
struct sock_filter *instrs, size_t len)
{
@@ -51,10 +63,7 @@ void append_filter_block(struct filter_block *head,
if (head->instrs == NULL) {
new_last = head;
} else {
- new_last = calloc(1, sizeof(struct filter_block));
- if (!new_last)
- die("could not allocate BPF filter block");
-
+ new_last = new_filter_block();
if (head->next != NULL) {
head->last->next = new_last;
head->last = new_last;
@@ -231,7 +240,7 @@ struct filter_block *compile_section(int nr, const char *policy_line,
* a disjunction ('||') of one or more conjunctions ('&&')
* of one or more atoms.
*
- * Atoms are of the form "arg{DNUM} OP NUM"
+ * Atoms are of the form "arg{DNUM} {OP} {NUM}"
* where:
* - DNUM is a decimal number.
* - OP is an operator: ==, !=, or & (flags set).
@@ -240,13 +249,19 @@ struct filter_block *compile_section(int nr, const char *policy_line,
* When the syscall arguments make the expression true,
* the syscall is allowed. If not, the process is killed.
*
- * To avoid killing the process, a policy line can include an
- * optional "return <errno>" clause:
+ * To block a syscall without killing the process,
+ * |policy_line| can be of the form:
+ * "return <errno>"
*
+ * This "return {NUM}" policy line will block the syscall,
+ * make it return -1 and set |errno| to NUM.
+ *
+ * A regular policy line can also include a "return <errno>" clause,
+ * separated by a semicolon (';'):
* "arg0 == 3 && arg1 == 5 || arg0 == 0x8; return {NUM}"
*
- * In this case, the syscall will return -1 and |errno| will
- * be set to NUM.
+ * If the syscall arguments don't make the expression true,
+ * the syscall will be blocked as above instead of killing the process.
*/
size_t len = 0;
@@ -256,35 +271,38 @@ struct filter_block *compile_section(int nr, const char *policy_line,
if (strlen(policy_line) >= MAX_POLICY_LINE_LENGTH)
return NULL;
- /* strtok() modifies its first argument, so let's make a copy. */
+ /* We will modify |policy_line|, so let's make a copy. */
char *line = strndup(policy_line, MAX_POLICY_LINE_LENGTH);
if (!line)
return NULL;
- /* Splits the optional "return <errno>" part. */
- char *line_ptr;
- char *arg_filter = strtok_r(line, ";", &line_ptr);
- char *ret_errno = strtok_r(NULL, ";", &line_ptr);
-
/*
- * We build the argument filter as a collection of smaller
+ * We build the filter section as a collection of smaller
* "filter blocks" linked together in a singly-linked list.
*/
- struct filter_block *head = calloc(1, sizeof(struct filter_block));
- if (!head)
- return NULL;
-
- head->instrs = NULL;
- head->last = head->next = NULL;
+ struct filter_block *head = new_filter_block();
/*
- * Argument filters begin with a label where the main filter
+ * Filter sections begin with a label where the main filter
* will jump after checking the syscall number.
*/
struct sock_filter *entry_label = new_instr_buf(ONE_INSTR);
set_bpf_lbl(entry_label, entry_lbl_id);
append_filter_block(head, entry_label, ONE_INSTR);
+ /* Checks whether we're unconditionally blocking this syscall. */
+ if (strncmp(line, "return", strlen("return")) == 0) {
+ if (compile_errno(head, line) < 0)
+ return NULL;
+ free(line);
+ return head;
+ }
+
+ /* Splits the optional "return <errno>" part. */
+ char *line_ptr;
+ char *arg_filter = strtok_r(line, ";", &line_ptr);
+ char *ret_errno = strtok_r(NULL, ";", &line_ptr);
+
/*
* Splits the policy line by '||' into conjunctions and each conjunction
* by '&&' into atoms.
@@ -354,11 +372,7 @@ int compile_filter(FILE *policy_file, struct sock_fprog *prog,
if (!policy_file)
return -1;
- struct filter_block *head = calloc(1, sizeof(struct filter_block));
- if (!head)
- return -1;
- head->instrs = NULL;
- head->last = head->next = NULL;
+ struct filter_block *head = new_filter_block();
struct filter_block *arg_blocks = NULL;
/* Start filter by validating arch. */