aboutsummaryrefslogtreecommitdiffhomepage
path: root/container
diff options
context:
space:
mode:
Diffstat (limited to 'container')
-rw-r--r--container/container.go4
-rw-r--r--container/container_test.go9
-rw-r--r--container/dispatcher.go16
-rw-r--r--container/dispatcher_test.go3
-rw-r--r--container/init.go4
-rw-r--r--container/mount.go2
-rw-r--r--container/seccomp/libseccomp-helper.c218
-rw-r--r--container/seccomp/libseccomp-helper.h23
-rw-r--r--container/seccomp/presets.go159
-rw-r--r--container/syscall.go2
-rw-r--r--container/syscall_386.go7
-rw-r--r--container/syscall_amd64.go7
-rw-r--r--container/sysctl.go69
-rw-r--r--container/sysctl_deprecated.go19
14 files changed, 288 insertions, 254 deletions
diff --git a/container/container.go b/container/container.go
index 3571d04a..6929c652 100644
--- a/container/container.go
+++ b/container/container.go
@@ -231,10 +231,10 @@ func (p *Container) Start() error {
// map to overflow id to work around ownership checks
if p.Uid < 1 {
- p.Uid = OverflowUid(p.msg)
+ p.Uid = OverflowUID()
}
if p.Gid < 1 {
- p.Gid = OverflowGid(p.msg)
+ p.Gid = OverflowGID()
}
if !p.RetainSession {
diff --git a/container/container_test.go b/container/container_test.go
index ca00fe7a..e70e23d3 100644
--- a/container/container_test.go
+++ b/container/container_test.go
@@ -342,8 +342,7 @@ var containerTestCases = []struct {
",upperdir="+
toHost(ctx.Value(testVal("upper")).(*check.Absolute).String())+
",workdir="+
- toHost(ctx.Value(testVal("work")).(*check.Absolute).String())+
- ",redirect_dir=nofollow,uuid=on,userxattr"),
+ toHost(ctx.Value(testVal("work")).(*check.Absolute).String())),
}
},
1 << 3, 1 << 14, nil, 0, std.PresetStrict},
@@ -396,8 +395,7 @@ var containerTestCases = []struct {
",lowerdir+="+
toHost(ctx.Value(testVal("lower0")).(*check.Absolute).String())+
",lowerdir+="+
- toHost(ctx.Value(testVal("lower1")).(*check.Absolute).String())+
- ",redirect_dir=nofollow,userxattr"),
+ toHost(ctx.Value(testVal("lower1")).(*check.Absolute).String())),
}
},
1 << 3, 1 << 14, nil, 0, std.PresetStrict},
@@ -828,6 +826,9 @@ func init() {
for _, s := range []string{
"seclabel",
"inode64",
+ "redirect_dir=nofollow",
+ "uuid=on",
+ "userxattr",
} {
cur.FsOptstr = strings.Replace(cur.FsOptstr, ","+s, "", 1)
mnt[i].FsOptstr = strings.Replace(mnt[i].FsOptstr, ","+s, "", 1)
diff --git a/container/dispatcher.go b/container/dispatcher.go
index 4636a469..ba5e9c10 100644
--- a/container/dispatcher.go
+++ b/container/dispatcher.go
@@ -37,15 +37,15 @@ type syscallDispatcher interface {
// lockOSThread provides [runtime.LockOSThread].
lockOSThread()
- // setPtracer provides [SetPtracer].
+ // setPtracer provides [ext.SetPtracer].
setPtracer(pid uintptr) error
- // setDumpable provides [SetDumpable].
+ // setDumpable provides [ext.SetDumpable].
setDumpable(dumpable uintptr) error
- // setNoNewPrivs provides [SetNoNewPrivs].
+ // setNoNewPrivs provides setNoNewPrivs.
setNoNewPrivs() error
- // lastcap provides [LastCap].
- lastcap(msg message.Msg) uintptr
+ // lastcap provides lastcap.
+ lastcap() uintptr
// capset provides capset.
capset(hdrp *capHeader, datap *[2]capData) error
// capBoundingSetDrop provides capBoundingSetDrop.
@@ -54,9 +54,9 @@ type syscallDispatcher interface {
capAmbientClearAll() error
// capAmbientRaise provides capAmbientRaise.
capAmbientRaise(cap uintptr) error
- // isatty provides [Isatty].
+ // isatty provides [ext.Isatty].
isatty(fd int) bool
- // receive provides [Receive].
+ // receive provides [params.Receive].
receive(key string, e any, fdp *int) (closeFunc func() error, err error)
// bindMount provides procPaths.bindMount.
@@ -152,7 +152,7 @@ func (direct) setPtracer(pid uintptr) error { return ext.SetPtracer(pid) }
func (direct) setDumpable(dumpable uintptr) error { return ext.SetDumpable(dumpable) }
func (direct) setNoNewPrivs() error { return setNoNewPrivs() }
-func (direct) lastcap(msg message.Msg) uintptr { return LastCap(msg) }
+func (direct) lastcap() uintptr { return lastcap() }
func (direct) capset(hdrp *capHeader, datap *[2]capData) error { return capset(hdrp, datap) }
func (direct) capBoundingSetDrop(cap uintptr) error { return capBoundingSetDrop(cap) }
func (direct) capAmbientClearAll() error { return capAmbientClearAll() }
diff --git a/container/dispatcher_test.go b/container/dispatcher_test.go
index 242af1bf..9276a2c5 100644
--- a/container/dispatcher_test.go
+++ b/container/dispatcher_test.go
@@ -352,9 +352,8 @@ func (k *kstub) setDumpable(dumpable uintptr) error {
}
func (k *kstub) setNoNewPrivs() error { k.Helper(); return k.Expects("setNoNewPrivs").Err }
-func (k *kstub) lastcap(msg message.Msg) uintptr {
+func (k *kstub) lastcap() uintptr {
k.Helper()
- k.checkMsg(msg)
return k.Expects("lastcap").Ret.(uintptr)
}
diff --git a/container/init.go b/container/init.go
index 07fff357..03501646 100644
--- a/container/init.go
+++ b/container/init.go
@@ -226,7 +226,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
}
// cache sysctl before pivot_root
- lastcap := k.lastcap(msg)
+ lc := k.lastcap()
if err := k.mount(zeroString, fhs.Root, zeroString, MS_SILENT|MS_SLAVE|MS_REC, zeroString); err != nil {
k.fatalf(msg, "cannot make / rslave: %v", optionalErrorUnwrap(err))
@@ -399,7 +399,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
if err := k.capAmbientClearAll(); err != nil {
k.fatalf(msg, "cannot clear the ambient capability set: %v", err)
}
- for i := range lastcap + 1 {
+ for i := range lc + 1 {
if slices.Contains(keepCaps, i) {
continue
}
diff --git a/container/mount.go b/container/mount.go
index 84415fa8..978b90a9 100644
--- a/container/mount.go
+++ b/container/mount.go
@@ -123,7 +123,7 @@ func (p procPaths) remount(msg message.Msg, target string, flags uintptr) error
{
var destFd int
if err := ext.IgnoringEINTR(func() (err error) {
- destFd, err = p.k.open(targetFinal, O_PATH|O_CLOEXEC, 0)
+ destFd, err = p.k.open(targetFinal, ext.O_PATH|O_CLOEXEC, 0)
return
}); err != nil {
return &os.PathError{Op: "open", Path: targetFinal, Err: err}
diff --git a/container/seccomp/libseccomp-helper.c b/container/seccomp/libseccomp-helper.c
index de8abc8d..7c640bac 100644
--- a/container/seccomp/libseccomp-helper.c
+++ b/container/seccomp/libseccomp-helper.c
@@ -9,130 +9,142 @@
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
-int32_t hakurei_scmp_make_filter(
- int *ret_p, uintptr_t allocate_p,
- 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;
- void *buf;
- size_t len = 0;
+int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
+ 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;
+ void *buf;
+ size_t len = 0;
- int32_t res = 0; /* refer to resPrefix for message */
+ 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},
- };
+ /* 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;
+ 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.
+ /* 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
+ 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;
- }
+ *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;
- }
- }
- }
+ 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);
+ 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 (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;
- }
- }
+ 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
+ /* 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;
+ 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));
+ 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 (allocate_p == 0) {
- *ret_p = seccomp_load(ctx);
- if (*ret_p != 0) {
- res = 7;
- goto out;
- }
- } else {
- *ret_p = seccomp_export_bpf_mem(ctx, NULL, &len);
- if (*ret_p != 0) {
- res = 6;
- goto out;
- }
+ if (allocate_p == 0) {
+ *ret_p = seccomp_load(ctx);
+ if (*ret_p != 0) {
+ res = 7;
+ goto out;
+ }
+ } else {
+ *ret_p = seccomp_export_bpf_mem(ctx, NULL, &len);
+ if (*ret_p != 0) {
+ res = 6;
+ goto out;
+ }
- buf = hakurei_scmp_allocate(allocate_p, len);
- if (buf == NULL) {
- res = 4;
- goto out;
- }
+ buf = hakurei_scmp_allocate(allocate_p, len);
+ if (buf == NULL) {
+ res = 4;
+ goto out;
+ }
- *ret_p = seccomp_export_bpf_mem(ctx, buf, &len);
- if (*ret_p != 0) {
- res = 6;
- goto out;
- }
- }
+ *ret_p = seccomp_export_bpf_mem(ctx, buf, &len);
+ if (*ret_p != 0) {
+ res = 6;
+ goto out;
+ }
+ }
out:
- if (ctx)
- seccomp_release(ctx);
+ if (ctx)
+ seccomp_release(ctx);
- return res;
+ return res;
}
diff --git a/container/seccomp/libseccomp-helper.h b/container/seccomp/libseccomp-helper.h
index 655110e4..29d5d5de 100644
--- a/container/seccomp/libseccomp-helper.h
+++ b/container/seccomp/libseccomp-helper.h
@@ -2,25 +2,24 @@
#include <stdint.h>
#if (SCMP_VER_MAJOR < 2) || (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 5) || \
- (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR == 5 && SCMP_VER_MICRO < 1)
+ (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR == 5 && SCMP_VER_MICRO < 1)
#error This package requires libseccomp >= v2.5.1
#endif
typedef enum {
- HAKUREI_EXPORT_MULTIARCH = 1 << 0,
- HAKUREI_EXPORT_CAN = 1 << 1,
- HAKUREI_EXPORT_BLUETOOTH = 1 << 2,
+ HAKUREI_EXPORT_MULTIARCH = 1 << 0,
+ HAKUREI_EXPORT_CAN = 1 << 1,
+ HAKUREI_EXPORT_BLUETOOTH = 1 << 2,
} hakurei_export_flag;
struct hakurei_syscall_rule {
- int syscall;
- int m_errno;
- struct scmp_arg_cmp *arg;
+ int syscall;
+ int m_errno;
+ struct scmp_arg_cmp *arg;
};
extern void *hakurei_scmp_allocate(uintptr_t f, size_t len);
-int32_t hakurei_scmp_make_filter(
- int *ret_p, uintptr_t allocate_p,
- uint32_t arch, uint32_t multiarch,
- struct hakurei_syscall_rule *rules,
- size_t rules_sz, hakurei_export_flag flags);
+int32_t hakurei_scmp_make_filter(int *ret_p, uintptr_t allocate_p,
+ uint32_t arch, uint32_t multiarch,
+ struct hakurei_syscall_rule *rules,
+ size_t rules_sz, hakurei_export_flag flags);
diff --git a/container/seccomp/presets.go b/container/seccomp/presets.go
index 4b76af61..252bf9f1 100644
--- a/container/seccomp/presets.go
+++ b/container/seccomp/presets.go
@@ -3,7 +3,7 @@ package seccomp
/* flatpak commit 4c3bf179e2e4a2a298cd1db1d045adaf3f564532 */
import (
- . "syscall"
+ "syscall"
. "hakurei.app/container/std"
. "hakurei.app/ext"
@@ -69,121 +69,124 @@ func Preset(presets FilterPreset, flags ExportFlag) (rules []NativeRule) {
var (
presetCommon = []NativeRule{
/* Block dmesg */
- {Syscall: SNR_SYSLOG, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_SYSLOG, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* Useless old syscall */
- {Syscall: SNR_USELIB, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_USELIB, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* Don't allow disabling accounting */
- {Syscall: SNR_ACCT, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_ACCT, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* Don't allow reading current quota use */
- {Syscall: SNR_QUOTACTL, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_QUOTACTL, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* Don't allow access to the kernel keyring */
- {Syscall: SNR_ADD_KEY, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_KEYCTL, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_REQUEST_KEY, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_ADD_KEY, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_KEYCTL, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_REQUEST_KEY, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* Scary VM/NUMA ops */
- {Syscall: SNR_MOVE_PAGES, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_MBIND, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_GET_MEMPOLICY, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SET_MEMPOLICY, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_MIGRATE_PAGES, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_MOVE_PAGES, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_MBIND, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_GET_MEMPOLICY, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SET_MEMPOLICY, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_MIGRATE_PAGES, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
}
/* hakurei: project-specific extensions */
presetCommonExt = []NativeRule{
/* system calls for changing the system clock */
- {Syscall: SNR_ADJTIMEX, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_CLOCK_ADJTIME, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_CLOCK_ADJTIME64, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_CLOCK_SETTIME, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_CLOCK_SETTIME64, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETTIMEOFDAY, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_ADJTIMEX, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_CLOCK_ADJTIME, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_CLOCK_ADJTIME64, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_CLOCK_SETTIME, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_CLOCK_SETTIME64, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETTIMEOFDAY, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* loading and unloading of kernel modules */
- {Syscall: SNR_DELETE_MODULE, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_FINIT_MODULE, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_INIT_MODULE, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_DELETE_MODULE, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_FINIT_MODULE, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_INIT_MODULE, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* system calls for rebooting and reboot preparation */
- {Syscall: SNR_KEXEC_FILE_LOAD, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_KEXEC_LOAD, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_REBOOT, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_KEXEC_FILE_LOAD, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_KEXEC_LOAD, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_REBOOT, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* system calls for enabling/disabling swap devices */
- {Syscall: SNR_SWAPOFF, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SWAPON, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_SWAPOFF, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SWAPON, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
}
presetNamespace = []NativeRule{
/* Don't allow subnamespace setups: */
- {Syscall: SNR_UNSHARE, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETNS, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_MOUNT, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_UMOUNT, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_UMOUNT2, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_PIVOT_ROOT, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_CHROOT, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_CLONE, Errno: ScmpErrno(EPERM),
- Arg: &ScmpArgCmp{Arg: cloneArg, Op: SCMP_CMP_MASKED_EQ, DatumA: CLONE_NEWUSER, DatumB: CLONE_NEWUSER}},
+ {Syscall: SNR_UNSHARE, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETNS, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_MOUNT, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_UMOUNT, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_UMOUNT2, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_PIVOT_ROOT, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_CHROOT, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_CLONE, Errno: ScmpErrno(syscall.EPERM),
+ Arg: &ScmpArgCmp{Arg: cloneArg, Op: SCMP_CMP_MASKED_EQ,
+ DatumA: syscall.CLONE_NEWUSER, DatumB: syscall.CLONE_NEWUSER}},
/* seccomp can't look into clone3()'s struct clone_args to check whether
* the flags are OK, so we have no choice but to block clone3().
- * Return ENOSYS so user-space will fall back to clone().
+ * Return syscall.ENOSYS so user-space will fall back to clone().
* (CVE-2021-41133; see also https://github.com/moby/moby/commit/9f6b562d)
*/
- {Syscall: SNR_CLONE3, Errno: ScmpErrno(ENOSYS), Arg: nil},
+ {Syscall: SNR_CLONE3, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
/* New mount manipulation APIs can also change our VFS. There's no
* legitimate reason to do these in the sandbox, so block all of them
* rather than thinking about which ones might be dangerous.
* (CVE-2021-41133) */
- {Syscall: SNR_OPEN_TREE, Errno: ScmpErrno(ENOSYS), Arg: nil},
- {Syscall: SNR_MOVE_MOUNT, Errno: ScmpErrno(ENOSYS), Arg: nil},
- {Syscall: SNR_FSOPEN, Errno: ScmpErrno(ENOSYS), Arg: nil},
- {Syscall: SNR_FSCONFIG, Errno: ScmpErrno(ENOSYS), Arg: nil},
- {Syscall: SNR_FSMOUNT, Errno: ScmpErrno(ENOSYS), Arg: nil},
- {Syscall: SNR_FSPICK, Errno: ScmpErrno(ENOSYS), Arg: nil},
- {Syscall: SNR_MOUNT_SETATTR, Errno: ScmpErrno(ENOSYS), Arg: nil},
+ {Syscall: SNR_OPEN_TREE, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
+ {Syscall: SNR_MOVE_MOUNT, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
+ {Syscall: SNR_FSOPEN, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
+ {Syscall: SNR_FSCONFIG, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
+ {Syscall: SNR_FSMOUNT, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
+ {Syscall: SNR_FSPICK, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
+ {Syscall: SNR_MOUNT_SETATTR, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
}
/* hakurei: project-specific extensions */
presetNamespaceExt = []NativeRule{
/* changing file ownership */
- {Syscall: SNR_CHOWN, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_CHOWN32, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_FCHOWN, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_FCHOWN32, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_FCHOWNAT, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_LCHOWN, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_LCHOWN32, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_CHOWN, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_CHOWN32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_FCHOWN, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_FCHOWN32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_FCHOWNAT, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_LCHOWN, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_LCHOWN32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* system calls for changing user ID and group ID credentials */
- {Syscall: SNR_SETGID, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETGID32, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETGROUPS, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETGROUPS32, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETREGID, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETREGID32, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETRESGID, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETRESGID32, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETRESUID, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETRESUID32, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETREUID, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETREUID32, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETUID, Errno: ScmpErrno(EPERM), Arg: nil},
- {Syscall: SNR_SETUID32, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_SETGID, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETGID32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETGROUPS, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETGROUPS32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETREGID, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETREGID32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETRESGID, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETRESGID32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETRESUID, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETRESUID32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETREUID, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETREUID32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETUID, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
+ {Syscall: SNR_SETUID32, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
}
presetTTY = []NativeRule{
/* Don't allow faking input to the controlling tty (CVE-2017-5226) */
- {Syscall: SNR_IOCTL, Errno: ScmpErrno(EPERM),
- Arg: &ScmpArgCmp{Arg: 1, Op: SCMP_CMP_MASKED_EQ, DatumA: 0xFFFFFFFF, DatumB: TIOCSTI}},
+ {Syscall: SNR_IOCTL, Errno: ScmpErrno(syscall.EPERM),
+ Arg: &ScmpArgCmp{Arg: 1, Op: SCMP_CMP_MASKED_EQ,
+ DatumA: 0xFFFFFFFF, DatumB: syscall.TIOCSTI}},
/* In the unlikely event that the controlling tty is a Linux virtual
* console (/dev/tty2 or similar), copy/paste operations have an effect
* similar to TIOCSTI (CVE-2023-28100) */
- {Syscall: SNR_IOCTL, Errno: ScmpErrno(EPERM),
- Arg: &ScmpArgCmp{Arg: 1, Op: SCMP_CMP_MASKED_EQ, DatumA: 0xFFFFFFFF, DatumB: TIOCLINUX}},
+ {Syscall: SNR_IOCTL, Errno: ScmpErrno(syscall.EPERM),
+ Arg: &ScmpArgCmp{Arg: 1, Op: SCMP_CMP_MASKED_EQ,
+ DatumA: 0xFFFFFFFF, DatumB: syscall.TIOCLINUX}},
}
presetEmu = []NativeRule{
@@ -191,15 +194,15 @@ var (
* so it's disabled as a hardening measure.
* However, it is required to run old 16-bit applications
* as well as some Wine patches, so it's allowed in multiarch. */
- {Syscall: SNR_MODIFY_LDT, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_MODIFY_LDT, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
}
/* hakurei: project-specific extensions */
presetEmuExt = []NativeRule{
- {Syscall: SNR_SUBPAGE_PROT, Errno: ScmpErrno(ENOSYS), Arg: nil},
- {Syscall: SNR_SWITCH_ENDIAN, Errno: ScmpErrno(ENOSYS), Arg: nil},
- {Syscall: SNR_VM86, Errno: ScmpErrno(ENOSYS), Arg: nil},
- {Syscall: SNR_VM86OLD, Errno: ScmpErrno(ENOSYS), Arg: nil},
+ {Syscall: SNR_SUBPAGE_PROT, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
+ {Syscall: SNR_SWITCH_ENDIAN, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
+ {Syscall: SNR_VM86, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
+ {Syscall: SNR_VM86OLD, Errno: ScmpErrno(syscall.ENOSYS), Arg: nil},
}
)
@@ -207,11 +210,11 @@ func presetDevel(allowedPersonality ScmpDatum) []NativeRule {
return []NativeRule{
/* Profiling operations; we expect these to be done by tools from outside
* the sandbox. In particular perf has been the source of many CVEs. */
- {Syscall: SNR_PERF_EVENT_OPEN, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_PERF_EVENT_OPEN, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
/* Don't allow you to switch to bsd emulation or whatnot */
- {Syscall: SNR_PERSONALITY, Errno: ScmpErrno(EPERM),
+ {Syscall: SNR_PERSONALITY, Errno: ScmpErrno(syscall.EPERM),
Arg: &ScmpArgCmp{Arg: 0, Op: SCMP_CMP_NE, DatumA: allowedPersonality}},
- {Syscall: SNR_PTRACE, Errno: ScmpErrno(EPERM), Arg: nil},
+ {Syscall: SNR_PTRACE, Errno: ScmpErrno(syscall.EPERM), Arg: nil},
}
}
diff --git a/container/syscall.go b/container/syscall.go
index e008ecea..43e49bea 100644
--- a/container/syscall.go
+++ b/container/syscall.go
@@ -9,7 +9,7 @@ import (
// setNoNewPrivs sets the calling thread's no_new_privs attribute.
func setNoNewPrivs() error {
- return ext.Prctl(PR_SET_NO_NEW_PRIVS, 1, 0)
+ return ext.Prctl(ext.PR_SET_NO_NEW_PRIVS, 1, 0)
}
// schedParam is equivalent to struct sched_param from include/linux/sched.h.
diff --git a/container/syscall_386.go b/container/syscall_386.go
deleted file mode 100644
index 4b8e2df3..00000000
--- a/container/syscall_386.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package container
-
-const (
- O_PATH = 0x200000
-
- PR_SET_NO_NEW_PRIVS = 0x26
-)
diff --git a/container/syscall_amd64.go b/container/syscall_amd64.go
deleted file mode 100644
index 4b8e2df3..00000000
--- a/container/syscall_amd64.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package container
-
-const (
- O_PATH = 0x200000
-
- PR_SET_NO_NEW_PRIVS = 0x26
-)
diff --git a/container/sysctl.go b/container/sysctl.go
index e4b7af23..2cf47258 100644
--- a/container/sysctl.go
+++ b/container/sysctl.go
@@ -2,14 +2,32 @@ package container
import (
"bytes"
+ "fmt"
"os"
"strconv"
"sync"
"hakurei.app/fhs"
- "hakurei.app/message"
)
+// mustSysctlInt reads a sysctl path via proc, interprets it as a decimal
+// representation of an integer, and terminates the program if the read was
+// unsuccessful or the response is invalid.
+func mustSysctlInt(path string) (v int) {
+ p, err := os.ReadFile(fhs.ProcSys + path)
+ if err != nil {
+ fmt.Println(err.Error())
+ os.Exit(1)
+ }
+
+ if v, err = strconv.Atoi(string(bytes.TrimSpace(p))); err != nil {
+ fmt.Printf("invalid sysctl response %q\n", string(p))
+ os.Exit(1)
+ }
+ return
+}
+
+// cached sysctl responses for populateSysctl
var (
kernelOverflowuid int
kernelOverflowgid int
@@ -18,34 +36,31 @@ var (
sysctlOnce sync.Once
)
-const (
- kernelOverflowuidPath = fhs.ProcSys + "kernel/overflowuid"
- kernelOverflowgidPath = fhs.ProcSys + "kernel/overflowgid"
- kernelCapLastCapPath = fhs.ProcSys + "kernel/cap_last_cap"
-)
-
-func mustReadSysctl(msg message.Msg) {
+// populateSysctl populates cached sysctl responses guarded by sysctlOnce.
+func populateSysctl() {
sysctlOnce.Do(func() {
- if v, err := os.ReadFile(kernelOverflowuidPath); err != nil {
- msg.GetLogger().Fatalf("cannot read %q: %v", kernelOverflowuidPath, err)
- } else if kernelOverflowuid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
- msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelOverflowuidPath, err)
- }
+ kernelOverflowuid = mustSysctlInt("kernel/overflowuid")
+ kernelOverflowgid = mustSysctlInt("kernel/overflowgid")
+ kernelCapLastCap = mustSysctlInt("kernel/cap_last_cap")
+ })
+}
- if v, err := os.ReadFile(kernelOverflowgidPath); err != nil {
- msg.GetLogger().Fatalf("cannot read %q: %v", kernelOverflowgidPath, err)
- } else if kernelOverflowgid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
- msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelOverflowgidPath, err)
- }
+// OverflowUID returns the fixed uid returned by legacy 16-bit UID system calls.
+// This value is also returned for unmapped UIDs in a user namespace.
+func OverflowUID() int {
+ populateSysctl()
+ return kernelOverflowuid
+}
- if v, err := os.ReadFile(kernelCapLastCapPath); err != nil {
- msg.GetLogger().Fatalf("cannot read %q: %v", kernelCapLastCapPath, err)
- } else if kernelCapLastCap, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
- msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelCapLastCapPath, err)
- }
- })
+// OverflowGID is like [OverflowUID], but for GID instead.
+func OverflowGID() int {
+ populateSysctl()
+ return kernelOverflowgid
}
-func OverflowUid(msg message.Msg) int { mustReadSysctl(msg); return kernelOverflowuid }
-func OverflowGid(msg message.Msg) int { mustReadSysctl(msg); return kernelOverflowgid }
-func LastCap(msg message.Msg) uintptr { mustReadSysctl(msg); return uintptr(kernelCapLastCap) }
+// lastcap returns the highest valid capability of the running kernel. Exports
+// CAP_LAST_CAP from the kernel.
+func lastcap() uintptr {
+ populateSysctl()
+ return uintptr(kernelCapLastCap)
+}
diff --git a/container/sysctl_deprecated.go b/container/sysctl_deprecated.go
new file mode 100644
index 00000000..3c76be2c
--- /dev/null
+++ b/container/sysctl_deprecated.go
@@ -0,0 +1,19 @@
+package container
+
+import "hakurei.app/message"
+
+// OverflowUid is the old function signature of [OverflowUID].
+//
+// Deprecated: This function will be removed in 0.5.
+func OverflowUid(_ message.Msg) int { return OverflowUID() }
+
+// OverflowGid is the old function signature of [OverflowGID].
+//
+// Deprecated: This function will be removed in 0.5.
+func OverflowGid(_ message.Msg) int { return OverflowGID() }
+
+// LastCap returns the highest valid capability of the running kernel. Exports
+// CAP_LAST_CAP from the kernel.
+//
+// Deprecated: This function will be unexported in 0.5.
+func LastCap(_ message.Msg) uintptr { return lastcap() }