diff options
Diffstat (limited to 'seccomp/libseccomp-helper.c')
| -rw-r--r-- | seccomp/libseccomp-helper.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/seccomp/libseccomp-helper.c b/seccomp/libseccomp-helper.c new file mode 100644 index 00000000..b09c3eb2 --- /dev/null +++ b/seccomp/libseccomp-helper.c @@ -0,0 +1,130 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* CLONE_NEWUSER */ +#endif + +#include "libseccomp-helper.h" +#include <assert.h> +#include <errno.h> +#include <sys/socket.h> + +#define LEN(arr) (sizeof(arr) / sizeof((arr)[0])) + +int32_t hakurei_export_filter(int *ret_p, int fd, uint32_t arch, + uint32_t multiarch, + struct hakurei_syscall_rule *rules, + size_t rules_sz, hakurei_export_flag flags) { + int i; + int last_allowed_family; + int disallowed; + struct hakurei_syscall_rule *rule; + + int32_t res = 0; /* refer to resPrefix for message */ + + /* Blocklist all but unix, inet, inet6 and netlink */ + struct { + int family; + hakurei_export_flag flags_mask; + } socket_family_allowlist[] = { + /* NOTE: Keep in numerical order */ + {AF_UNSPEC, 0}, + {AF_LOCAL, 0}, + {AF_INET, 0}, + {AF_INET6, 0}, + {AF_NETLINK, 0}, + {AF_CAN, HAKUREI_EXPORT_CAN}, + {AF_BLUETOOTH, HAKUREI_EXPORT_BLUETOOTH}, + }; + + scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW); + if (ctx == NULL) { + res = 1; + goto out; + } else + errno = 0; + + /* We only really need to handle arches on multiarch systems. + * If only one arch is supported the default is fine */ + if (arch != 0) { + /* This *adds* the target arch, instead of replacing the + * native one. This is not ideal, because we'd like to only + * allow the target arch, but we can't really disallow the + * native arch at this point, because then bubblewrap + * couldn't continue running. */ + *ret_p = seccomp_arch_add(ctx, arch); + if (*ret_p < 0 && *ret_p != -EEXIST) { + res = 2; + goto out; + } + + if (flags & HAKUREI_EXPORT_MULTIARCH && multiarch != 0) { + *ret_p = seccomp_arch_add(ctx, multiarch); + if (*ret_p < 0 && *ret_p != -EEXIST) { + res = 3; + goto out; + } + } + } + + for (i = 0; i < rules_sz; i++) { + rule = &rules[i]; + assert(rule->m_errno == EPERM || rule->m_errno == ENOSYS); + + if (rule->arg) + *ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno), + rule->syscall, 1, *rule->arg); + else + *ret_p = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(rule->m_errno), + rule->syscall, 0); + + if (*ret_p == -EFAULT) { + res = 4; + goto out; + } else if (*ret_p < 0) { + res = 5; + goto out; + } + } + + /* Socket filtering doesn't work on e.g. i386, so ignore failures here + * However, we need to user seccomp_rule_add_exact to avoid libseccomp doing + * something else: https://github.com/seccomp/libseccomp/issues/8 */ + last_allowed_family = -1; + for (i = 0; i < LEN(socket_family_allowlist); i++) { + if (socket_family_allowlist[i].flags_mask != 0 && + (socket_family_allowlist[i].flags_mask & flags) != + socket_family_allowlist[i].flags_mask) + continue; + + for (disallowed = last_allowed_family + 1; + disallowed < socket_family_allowlist[i].family; disallowed++) { + /* Blocklist the in-between valid families */ + seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), + SCMP_SYS(socket), 1, + SCMP_A0(SCMP_CMP_EQ, disallowed)); + } + last_allowed_family = socket_family_allowlist[i].family; + } + /* Blocklist the rest */ + seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EAFNOSUPPORT), SCMP_SYS(socket), 1, + SCMP_A0(SCMP_CMP_GE, last_allowed_family + 1)); + + if (fd < 0) { + *ret_p = seccomp_load(ctx); + if (*ret_p != 0) { + res = 7; + goto out; + } + } else { + *ret_p = seccomp_export_bpf(ctx, fd); + if (*ret_p != 0) { + res = 6; + goto out; + } + } + +out: + if (ctx) + seccomp_release(ctx); + + return res; +} |
