aboutsummaryrefslogtreecommitdiffhomepage
path: root/container/seccomp/libseccomp-helper.c
blob: b09c3eb2a51e865fdbd65632189ca9a270b08306 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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;
}