landlock 0.2 → 0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "mkmf"
4
4
 
5
+ append_cppflags("-D_GNU_SOURCE")
6
+
5
7
  abort "missing ruby headers" unless have_header("ruby.h")
6
8
 
7
9
  have_header("linux/landlock.h")
@@ -17,6 +19,7 @@ create_makefile("landlock/landlock")
17
19
  if RUBY_PLATFORM.include?("linux")
18
20
  helper = "landlock-safe-exec"
19
21
  helper_src = "$(srcdir)/bin/safe_exec_helper.c"
22
+ helper_headers = "$(srcdir)/landlock_native.h $(srcdir)/seccomp_deny_network.h"
20
23
  helper_dest = "$(RUBYARCHDIR)/#{helper}"
21
24
 
22
25
  File.open("Makefile", "a") do |makefile|
@@ -25,7 +28,7 @@ if RUBY_PLATFORM.include?("linux")
25
28
  all: #{helper}
26
29
 
27
30
  #{helper}: #{helper_src}
28
- \t$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) #{helper_src} -o #{helper} $(LIBS)
31
+ \t$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) #{helper_src} -o #{helper}
29
32
 
30
33
  install: install-#{helper}
31
34
 
@@ -1,5 +1,6 @@
1
1
  #include "ruby.h"
2
2
  #include "landlock_native.h"
3
+ #include "seccomp_deny_network.h"
3
4
 
4
5
  #include <string.h>
5
6
 
@@ -9,8 +10,7 @@ static VALUE eSyscallError;
9
10
 
10
11
  static void raise_syscall_error(const char *syscall_name) {
11
12
  int saved_errno = errno;
12
- VALUE err = rb_funcall(eSyscallError, rb_intern("new"), 3,
13
- rb_str_new_cstr(syscall_name),
13
+ VALUE err = rb_funcall(eSyscallError, rb_intern("new"), 3, rb_str_new_cstr(syscall_name),
14
14
  INT2NUM(saved_errno),
15
15
  rb_sprintf("%s failed: %s", syscall_name, strerror(saved_errno)));
16
16
  rb_exc_raise(err);
@@ -19,7 +19,9 @@ static void raise_syscall_error(const char *syscall_name) {
19
19
  static VALUE rb_ll_abi_version(VALUE self) {
20
20
  long abi = ll_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
21
21
  if (abi < 0) {
22
- if (errno == ENOSYS || errno == EOPNOTSUPP) return INT2FIX(0);
22
+ if (errno == ENOSYS || errno == EOPNOTSUPP) {
23
+ return INT2FIX(0);
24
+ }
23
25
  raise_syscall_error("landlock_create_ruleset");
24
26
  }
25
27
  return LONG2NUM(abi);
@@ -45,7 +47,9 @@ static VALUE rb_ll_create_ruleset(int argc, VALUE *argv, VALUE self) {
45
47
  attr.scoped = scoped;
46
48
 
47
49
  long fd = ll_create_ruleset(&attr, attr_size, 0);
48
- if (fd < 0) raise_syscall_error("landlock_create_ruleset");
50
+ if (fd < 0) {
51
+ raise_syscall_error("landlock_create_ruleset");
52
+ }
49
53
  return INT2NUM(fd);
50
54
  }
51
55
 
@@ -55,7 +59,9 @@ static VALUE rb_ll_add_path_rule(VALUE self, VALUE ruleset_fd, VALUE path, VALUE
55
59
  Check_Type(path, T_STRING);
56
60
  const char *cpath = StringValueCStr(path);
57
61
  int parent_fd = open(cpath, O_PATH | O_CLOEXEC);
58
- if (parent_fd < 0) raise_syscall_error("open");
62
+ if (parent_fd < 0) {
63
+ raise_syscall_error("open");
64
+ }
59
65
 
60
66
  struct rb_landlock_path_beneath_attr rule;
61
67
  memset(&rule, 0, sizeof(rule));
@@ -74,7 +80,9 @@ static VALUE rb_ll_add_path_rule(VALUE self, VALUE ruleset_fd, VALUE path, VALUE
74
80
 
75
81
  static VALUE rb_ll_add_net_rule(VALUE self, VALUE ruleset_fd, VALUE port, VALUE access_bits) {
76
82
  unsigned long long p = NUM2ULL(port);
77
- if (p > 65535ULL) rb_raise(rb_eArgError, "TCP port must be between 0 and 65535");
83
+ if (p > 65535ULL) {
84
+ rb_raise(rb_eArgError, "TCP port must be between 0 and 65535");
85
+ }
78
86
 
79
87
  struct rb_landlock_net_port_attr rule;
80
88
  memset(&rule, 0, sizeof(rule));
@@ -82,7 +90,9 @@ static VALUE rb_ll_add_net_rule(VALUE self, VALUE ruleset_fd, VALUE port, VALUE
82
90
  rule.port = p;
83
91
 
84
92
  long ret = ll_add_rule(NUM2INT(ruleset_fd), LANDLOCK_RULE_NET_PORT, &rule, 0);
85
- if (ret < 0) raise_syscall_error("landlock_add_rule(net_port)");
93
+ if (ret < 0) {
94
+ raise_syscall_error("landlock_add_rule(net_port)");
95
+ }
86
96
  return Qtrue;
87
97
  }
88
98
 
@@ -93,7 +103,9 @@ static VALUE rb_ll_restrict_self(VALUE self, VALUE ruleset_fd) {
93
103
  }
94
104
 
95
105
  long ret = ll_restrict_self(NUM2INT(ruleset_fd), 0);
96
- if (ret < 0) raise_syscall_error("landlock_restrict_self");
106
+ if (ret < 0) {
107
+ raise_syscall_error("landlock_restrict_self");
108
+ }
97
109
  return Qtrue;
98
110
  #else
99
111
  errno = ENOSYS;
@@ -103,10 +115,20 @@ static VALUE rb_ll_restrict_self(VALUE self, VALUE ruleset_fd) {
103
115
 
104
116
  static VALUE rb_ll_close_fd(VALUE self, VALUE fd_value) {
105
117
  int fd = NUM2INT(fd_value);
106
- if (fd >= 0) close(fd);
118
+ if (fd >= 0) {
119
+ close(fd);
120
+ }
107
121
  return Qnil;
108
122
  }
109
123
 
124
+ static VALUE rb_ll_seccomp_deny_network(VALUE self) {
125
+ const char *error_message = "seccomp(SECCOMP_SET_MODE_FILTER)";
126
+ if (rb_landlock_seccomp_deny_network(&error_message) != 0) {
127
+ raise_syscall_error(error_message);
128
+ }
129
+ return Qtrue;
130
+ }
131
+
110
132
  void Init_landlock(void) {
111
133
  mLandlock = rb_define_module("Landlock");
112
134
 
@@ -128,6 +150,7 @@ void Init_landlock(void) {
128
150
  rb_define_singleton_method(mLandlock, "_add_net_rule", rb_ll_add_net_rule, 3);
129
151
  rb_define_singleton_method(mLandlock, "_restrict_self", rb_ll_restrict_self, 1);
130
152
  rb_define_singleton_method(mLandlock, "_close_fd", rb_ll_close_fd, 1);
153
+ rb_define_singleton_method(mLandlock, "seccomp_deny_network!", rb_ll_seccomp_deny_network, 0);
131
154
 
132
155
  rb_define_const(mLandlock, "ACCESS_FS_EXECUTE", ULL2NUM(LANDLOCK_ACCESS_FS_EXECUTE));
133
156
  rb_define_const(mLandlock, "ACCESS_FS_WRITE_FILE", ULL2NUM(LANDLOCK_ACCESS_FS_WRITE_FILE));
@@ -147,6 +170,7 @@ void Init_landlock(void) {
147
170
  rb_define_const(mLandlock, "ACCESS_FS_IOCTL_DEV", ULL2NUM(LANDLOCK_ACCESS_FS_IOCTL_DEV));
148
171
  rb_define_const(mLandlock, "ACCESS_NET_BIND_TCP", ULL2NUM(LANDLOCK_ACCESS_NET_BIND_TCP));
149
172
  rb_define_const(mLandlock, "ACCESS_NET_CONNECT_TCP", ULL2NUM(LANDLOCK_ACCESS_NET_CONNECT_TCP));
150
- rb_define_const(mLandlock, "SCOPE_ABSTRACT_UNIX_SOCKET", ULL2NUM(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET));
173
+ rb_define_const(mLandlock, "SCOPE_ABSTRACT_UNIX_SOCKET",
174
+ ULL2NUM(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET));
151
175
  rb_define_const(mLandlock, "SCOPE_SIGNAL", ULL2NUM(LANDLOCK_SCOPE_SIGNAL));
152
176
  }
@@ -16,22 +16,23 @@
16
16
  #endif
17
17
 
18
18
  #ifndef SYS_landlock_create_ruleset
19
- # if defined(__linux__) && defined(__NR_landlock_create_ruleset) && defined(__NR_landlock_add_rule) && defined(__NR_landlock_restrict_self)
20
- # define SYS_landlock_create_ruleset __NR_landlock_create_ruleset
21
- # define SYS_landlock_add_rule __NR_landlock_add_rule
22
- # define SYS_landlock_restrict_self __NR_landlock_restrict_self
23
- # elif defined(__linux__) && defined(__x86_64__) && defined(__ILP32__)
24
- # ifndef __X32_SYSCALL_BIT
25
- # define __X32_SYSCALL_BIT 0x40000000
26
- # endif
27
- # define SYS_landlock_create_ruleset (__X32_SYSCALL_BIT + 444)
28
- # define SYS_landlock_add_rule (__X32_SYSCALL_BIT + 445)
29
- # define SYS_landlock_restrict_self (__X32_SYSCALL_BIT + 446)
30
- # elif defined(__linux__) && (defined(__x86_64__) || defined(__aarch64__) || defined(__i386__))
31
- # define SYS_landlock_create_ruleset 444
32
- # define SYS_landlock_add_rule 445
33
- # define SYS_landlock_restrict_self 446
34
- # endif
19
+ #if defined(__linux__) && defined(__NR_landlock_create_ruleset) && \
20
+ defined(__NR_landlock_add_rule) && defined(__NR_landlock_restrict_self)
21
+ #define SYS_landlock_create_ruleset __NR_landlock_create_ruleset
22
+ #define SYS_landlock_add_rule __NR_landlock_add_rule
23
+ #define SYS_landlock_restrict_self __NR_landlock_restrict_self
24
+ #elif defined(__linux__) && defined(__x86_64__) && defined(__ILP32__)
25
+ #ifndef __X32_SYSCALL_BIT
26
+ #define __X32_SYSCALL_BIT 0x40000000
27
+ #endif
28
+ #define SYS_landlock_create_ruleset (__X32_SYSCALL_BIT + 444)
29
+ #define SYS_landlock_add_rule (__X32_SYSCALL_BIT + 445)
30
+ #define SYS_landlock_restrict_self (__X32_SYSCALL_BIT + 446)
31
+ #elif defined(__linux__) && (defined(__x86_64__) || defined(__aarch64__) || defined(__i386__))
32
+ #define SYS_landlock_create_ruleset 444
33
+ #define SYS_landlock_add_rule 445
34
+ #define SYS_landlock_restrict_self 446
35
+ #endif
35
36
  #endif
36
37
 
37
38
  #ifndef LANDLOCK_CREATE_RULESET_VERSION
@@ -47,16 +48,16 @@
47
48
  #endif
48
49
 
49
50
  #ifndef LANDLOCK_ACCESS_FS_EXECUTE
50
- #define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0)
51
+ #define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0)
51
52
  #endif
52
53
  #ifndef LANDLOCK_ACCESS_FS_WRITE_FILE
53
54
  #define LANDLOCK_ACCESS_FS_WRITE_FILE (1ULL << 1)
54
55
  #endif
55
56
  #ifndef LANDLOCK_ACCESS_FS_READ_FILE
56
- #define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2)
57
+ #define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2)
57
58
  #endif
58
59
  #ifndef LANDLOCK_ACCESS_FS_READ_DIR
59
- #define LANDLOCK_ACCESS_FS_READ_DIR (1ULL << 3)
60
+ #define LANDLOCK_ACCESS_FS_READ_DIR (1ULL << 3)
60
61
  #endif
61
62
  #ifndef LANDLOCK_ACCESS_FS_REMOVE_DIR
62
63
  #define LANDLOCK_ACCESS_FS_REMOVE_DIR (1ULL << 4)
@@ -65,38 +66,38 @@
65
66
  #define LANDLOCK_ACCESS_FS_REMOVE_FILE (1ULL << 5)
66
67
  #endif
67
68
  #ifndef LANDLOCK_ACCESS_FS_MAKE_CHAR
68
- #define LANDLOCK_ACCESS_FS_MAKE_CHAR (1ULL << 6)
69
+ #define LANDLOCK_ACCESS_FS_MAKE_CHAR (1ULL << 6)
69
70
  #endif
70
71
  #ifndef LANDLOCK_ACCESS_FS_MAKE_DIR
71
- #define LANDLOCK_ACCESS_FS_MAKE_DIR (1ULL << 7)
72
+ #define LANDLOCK_ACCESS_FS_MAKE_DIR (1ULL << 7)
72
73
  #endif
73
74
  #ifndef LANDLOCK_ACCESS_FS_MAKE_REG
74
- #define LANDLOCK_ACCESS_FS_MAKE_REG (1ULL << 8)
75
+ #define LANDLOCK_ACCESS_FS_MAKE_REG (1ULL << 8)
75
76
  #endif
76
77
  #ifndef LANDLOCK_ACCESS_FS_MAKE_SOCK
77
- #define LANDLOCK_ACCESS_FS_MAKE_SOCK (1ULL << 9)
78
+ #define LANDLOCK_ACCESS_FS_MAKE_SOCK (1ULL << 9)
78
79
  #endif
79
80
  #ifndef LANDLOCK_ACCESS_FS_MAKE_FIFO
80
- #define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10)
81
+ #define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10)
81
82
  #endif
82
83
  #ifndef LANDLOCK_ACCESS_FS_MAKE_BLOCK
83
84
  #define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11)
84
85
  #endif
85
86
  #ifndef LANDLOCK_ACCESS_FS_MAKE_SYM
86
- #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12)
87
+ #define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12)
87
88
  #endif
88
89
  #ifndef LANDLOCK_ACCESS_FS_REFER
89
- #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
90
+ #define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
90
91
  #endif
91
92
  #ifndef LANDLOCK_ACCESS_FS_TRUNCATE
92
- #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
93
+ #define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
93
94
  #endif
94
95
  #ifndef LANDLOCK_ACCESS_FS_IOCTL_DEV
95
- #define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15)
96
+ #define LANDLOCK_ACCESS_FS_IOCTL_DEV (1ULL << 15)
96
97
  #endif
97
98
 
98
99
  #ifndef LANDLOCK_ACCESS_NET_BIND_TCP
99
- #define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0)
100
+ #define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0)
100
101
  #endif
101
102
  #ifndef LANDLOCK_ACCESS_NET_CONNECT_TCP
102
103
  #define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1)
@@ -0,0 +1,176 @@
1
+ #ifndef RB_LANDLOCK_SECCOMP_DENY_NETWORK_H
2
+ #define RB_LANDLOCK_SECCOMP_DENY_NETWORK_H
3
+
4
+ #include <errno.h>
5
+ #include <stddef.h>
6
+ #include <stdlib.h>
7
+ #include <unistd.h>
8
+
9
+ #ifdef __linux__
10
+ #include <linux/audit.h>
11
+ #include <linux/filter.h>
12
+ #include <linux/seccomp.h>
13
+ #include <sys/prctl.h>
14
+ #include <sys/syscall.h>
15
+ #endif
16
+
17
+ #ifndef SECCOMP_RET_ALLOW
18
+ #define SECCOMP_RET_ALLOW 0x7fff0000U
19
+ #endif
20
+ #ifndef SECCOMP_RET_ERRNO
21
+ #define SECCOMP_RET_ERRNO 0x00050000U
22
+ #endif
23
+ #ifndef SECCOMP_RET_KILL_PROCESS
24
+ #define SECCOMP_RET_KILL_PROCESS 0x80000000U
25
+ #endif
26
+ #ifndef SECCOMP_SET_MODE_FILTER
27
+ #define SECCOMP_SET_MODE_FILTER 1
28
+ #endif
29
+
30
+ #ifdef __linux__
31
+ static int rb_landlock_deny_network_syscalls[] = {
32
+ #ifdef __NR_socket
33
+ __NR_socket,
34
+ #endif
35
+ #ifdef __NR_socketpair
36
+ __NR_socketpair,
37
+ #endif
38
+ #ifdef __NR_connect
39
+ __NR_connect,
40
+ #endif
41
+ #ifdef __NR_bind
42
+ __NR_bind,
43
+ #endif
44
+ #ifdef __NR_listen
45
+ __NR_listen,
46
+ #endif
47
+ #ifdef __NR_accept
48
+ __NR_accept,
49
+ #endif
50
+ #ifdef __NR_accept4
51
+ __NR_accept4,
52
+ #endif
53
+ #ifdef __NR_sendto
54
+ __NR_sendto,
55
+ #endif
56
+ #ifdef __NR_sendmsg
57
+ __NR_sendmsg,
58
+ #endif
59
+ #ifdef __NR_sendmmsg
60
+ __NR_sendmmsg,
61
+ #endif
62
+ #ifdef __NR_recvfrom
63
+ __NR_recvfrom,
64
+ #endif
65
+ #ifdef __NR_recvmsg
66
+ __NR_recvmsg,
67
+ #endif
68
+ #ifdef __NR_recvmmsg
69
+ __NR_recvmmsg,
70
+ #endif
71
+ #ifdef __NR_socketcall
72
+ __NR_socketcall,
73
+ #endif
74
+ };
75
+
76
+ #if defined(__x86_64__) && defined(AUDIT_ARCH_X86_64)
77
+ #define RB_LANDLOCK_EXPECTED_AUDIT_ARCH AUDIT_ARCH_X86_64
78
+ #elif defined(__aarch64__) && defined(AUDIT_ARCH_AARCH64)
79
+ #define RB_LANDLOCK_EXPECTED_AUDIT_ARCH AUDIT_ARCH_AARCH64
80
+ #elif defined(__i386__) && defined(AUDIT_ARCH_I386)
81
+ #define RB_LANDLOCK_EXPECTED_AUDIT_ARCH AUDIT_ARCH_I386
82
+ #endif
83
+
84
+ #if defined(__x86_64__) && !defined(__ILP32__)
85
+ #ifndef __X32_SYSCALL_BIT
86
+ #define __X32_SYSCALL_BIT 0x40000000
87
+ #endif
88
+ #define RB_LANDLOCK_DENY_X32_SYSCALLS 1
89
+ #endif
90
+ #endif
91
+
92
+ static int rb_landlock_seccomp_deny_network(const char **error_message) {
93
+ #ifdef __linux__
94
+ #ifndef RB_LANDLOCK_EXPECTED_AUDIT_ARCH
95
+ errno = ENOSYS;
96
+ if (error_message) {
97
+ *error_message = "seccomp unsupported architecture";
98
+ }
99
+ return -1;
100
+ #else
101
+ size_t count =
102
+ sizeof(rb_landlock_deny_network_syscalls) / sizeof(rb_landlock_deny_network_syscalls[0]);
103
+ if (count == 0) {
104
+ return 0;
105
+ }
106
+
107
+ size_t len = 1 + (2 * count) + 1;
108
+ len += 3;
109
+ #ifdef RB_LANDLOCK_DENY_X32_SYSCALLS
110
+ len += 2;
111
+ #endif
112
+ struct sock_filter *filter = calloc(len, sizeof(struct sock_filter));
113
+ if (!filter) {
114
+ if (error_message) {
115
+ *error_message = "calloc";
116
+ }
117
+ return -1;
118
+ }
119
+
120
+ size_t pc = 0;
121
+ filter[pc++] =
122
+ (struct sock_filter)BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch));
123
+ filter[pc++] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,
124
+ RB_LANDLOCK_EXPECTED_AUDIT_ARCH, 1, 0);
125
+ filter[pc++] = (struct sock_filter)BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS);
126
+ filter[pc++] =
127
+ (struct sock_filter)BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr));
128
+ #ifdef RB_LANDLOCK_DENY_X32_SYSCALLS
129
+ filter[pc++] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, __X32_SYSCALL_BIT, 0, 1);
130
+ filter[pc++] = (struct sock_filter)BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EPERM);
131
+ #endif
132
+ for (size_t i = 0; i < count; i++) {
133
+ filter[pc++] = (struct sock_filter)BPF_JUMP(
134
+ BPF_JMP | BPF_JEQ | BPF_K, (unsigned int)rb_landlock_deny_network_syscalls[i], 0, 1);
135
+ filter[pc++] = (struct sock_filter)BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EPERM);
136
+ }
137
+ filter[pc++] = (struct sock_filter)BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
138
+
139
+ struct sock_fprog prog;
140
+ prog.len = (unsigned short)pc;
141
+ prog.filter = filter;
142
+
143
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
144
+ if (error_message) {
145
+ *error_message = "prctl(PR_SET_NO_NEW_PRIVS)";
146
+ }
147
+ free(filter);
148
+ return -1;
149
+ }
150
+ #ifdef SYS_seccomp
151
+ if (syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog) == 0) {
152
+ free(filter);
153
+ return 0;
154
+ }
155
+ #endif
156
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) {
157
+ if (error_message) {
158
+ *error_message = "seccomp(SECCOMP_SET_MODE_FILTER)";
159
+ }
160
+ free(filter);
161
+ return -1;
162
+ }
163
+
164
+ free(filter);
165
+ return 0;
166
+ #endif
167
+ #else
168
+ errno = ENOSYS;
169
+ if (error_message) {
170
+ *error_message = "seccomp(SECCOMP_SET_MODE_FILTER)";
171
+ }
172
+ return -1;
173
+ #endif
174
+ }
175
+
176
+ #endif
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Landlock
4
+ module Env
5
+ module_function
6
+
7
+ def normalize(env)
8
+ return nil if env.nil?
9
+
10
+ raise ArgumentError, "env must be a Hash-compatible object" unless env.respond_to?(:each_pair)
11
+
12
+ normalized = {}
13
+ env.each_pair do |key, value|
14
+ key = key.to_s
15
+ raise ArgumentError, "env key must not be empty" if key.empty?
16
+ raise ArgumentError, "env key must not contain '='" if key.include?("=")
17
+ raise ArgumentError, "env key must not contain NUL" if key.include?("\0")
18
+
19
+ if value.nil?
20
+ normalized[key] = nil
21
+ else
22
+ value = value.to_s
23
+ raise ArgumentError, "env value must not contain NUL" if value.include?("\0")
24
+
25
+ normalized[key] = value
26
+ end
27
+ end
28
+ normalized
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Landlock
4
+ Error = Class.new(StandardError)
5
+ UnsupportedError = Class.new(Error)
6
+
7
+ class SyscallError < Error
8
+ attr_reader :errno, :syscall
9
+
10
+ def initialize(syscall, errno, message = nil)
11
+ @syscall = syscall
12
+ @errno = errno
13
+ super(message || "#{syscall} failed: #{errno}")
14
+ end
15
+ end
16
+
17
+ class CommandError < Error
18
+ attr_reader :stdout, :stderr, :status, :result
19
+
20
+ def initialize(message, stdout: "", stderr: "", status: nil, result: nil)
21
+ @stdout = stdout
22
+ @stderr = stderr
23
+ @status = status
24
+ @result = result
25
+ super(message)
26
+ end
27
+ end
28
+
29
+ class OutputTooLargeError < Error
30
+ attr_accessor :result
31
+ end
32
+ end