seccomp-ruby 0.1.0
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.
- checksums.yaml +7 -0
- data/.rubocop.yml +43 -0
- data/.yardopts +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +132 -0
- data/Rakefile +34 -0
- data/examples/arg_filter.rb +18 -0
- data/examples/basic_allowlist.rb +20 -0
- data/examples/errno_filter.rb +18 -0
- data/examples/notify_supervisor.rb +26 -0
- data/ext/seccomp_ext/const.c +133 -0
- data/ext/seccomp_ext/extconf.rb +45 -0
- data/ext/seccomp_ext/filter.c +685 -0
- data/ext/seccomp_ext/notify.c +184 -0
- data/ext/seccomp_ext/seccomp_ext.c +20 -0
- data/ext/seccomp_ext/seccomp_ext.h +35 -0
- data/ext/seccomp_ext/syscall.c +86 -0
- data/ext/seccomp_ext/util.c +110 -0
- data/lib/libseccomp.rb +77 -0
- data/lib/seccomp/action.rb +154 -0
- data/lib/seccomp/arch.rb +48 -0
- data/lib/seccomp/arg.rb +107 -0
- data/lib/seccomp/attributes.rb +127 -0
- data/lib/seccomp/dsl.rb +33 -0
- data/lib/seccomp/errors.rb +83 -0
- data/lib/seccomp/filter.rb +425 -0
- data/lib/seccomp/low_level.rb +210 -0
- data/lib/seccomp/notification.rb +95 -0
- data/lib/seccomp/notifier.rb +187 -0
- data/lib/seccomp/syscall.rb +44 -0
- data/lib/seccomp/version.rb +6 -0
- data/lib/seccomp.rb +3 -0
- data/sig/seccomp.rbs +300 -0
- metadata +75 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#include "seccomp_ext.h"
|
|
2
|
+
#include <errno.h>
|
|
3
|
+
#include <ruby/thread.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
#include <sys/ioctl.h>
|
|
6
|
+
|
|
7
|
+
struct seccomp_receive_args {
|
|
8
|
+
int fd;
|
|
9
|
+
struct seccomp_notif *request;
|
|
10
|
+
int rc;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
static void *
|
|
14
|
+
seccomp_ext_notify_receive_without_gvl(void *pointer)
|
|
15
|
+
{
|
|
16
|
+
struct seccomp_receive_args *args = pointer;
|
|
17
|
+
|
|
18
|
+
args->rc = seccomp_notify_receive(args->fd, args->request);
|
|
19
|
+
return NULL;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static VALUE
|
|
23
|
+
seccomp_ext_notify_receive_free(VALUE pointer)
|
|
24
|
+
{
|
|
25
|
+
struct seccomp_receive_args *args = (struct seccomp_receive_args *)pointer;
|
|
26
|
+
|
|
27
|
+
seccomp_notify_free(args->request, NULL);
|
|
28
|
+
args->request = NULL;
|
|
29
|
+
return Qnil;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static VALUE
|
|
33
|
+
seccomp_ext_notify_receive_call(VALUE pointer)
|
|
34
|
+
{
|
|
35
|
+
struct seccomp_receive_args *args = (struct seccomp_receive_args *)pointer;
|
|
36
|
+
VALUE result;
|
|
37
|
+
VALUE values;
|
|
38
|
+
uint64_t request_args[6];
|
|
39
|
+
int index;
|
|
40
|
+
|
|
41
|
+
do {
|
|
42
|
+
rb_thread_call_without_gvl(seccomp_ext_notify_receive_without_gvl,
|
|
43
|
+
args, RUBY_UBF_IO, NULL);
|
|
44
|
+
} while (args->rc == -EINTR);
|
|
45
|
+
if (args->rc < 0)
|
|
46
|
+
return rb_ary_new_from_args(2, INT2NUM(args->rc), Qnil);
|
|
47
|
+
|
|
48
|
+
result = rb_hash_new();
|
|
49
|
+
rb_hash_aset(result, ID2SYM(rb_intern("id")), ULL2NUM(args->request->id));
|
|
50
|
+
rb_hash_aset(result, ID2SYM(rb_intern("pid")), UINT2NUM(args->request->pid));
|
|
51
|
+
rb_hash_aset(result, ID2SYM(rb_intern("flags")), UINT2NUM(args->request->flags));
|
|
52
|
+
rb_hash_aset(result, ID2SYM(rb_intern("syscall")), INT2NUM(args->request->data.nr));
|
|
53
|
+
rb_hash_aset(result, ID2SYM(rb_intern("arch")),
|
|
54
|
+
seccomp_ext_uint32(args->request->data.arch));
|
|
55
|
+
rb_hash_aset(result, ID2SYM(rb_intern("instruction_pointer")),
|
|
56
|
+
ULL2NUM(args->request->data.instruction_pointer));
|
|
57
|
+
memcpy(request_args, args->request->data.args, sizeof(request_args));
|
|
58
|
+
values = rb_ary_new_capa(6);
|
|
59
|
+
for (index = 0; index < 6; index++)
|
|
60
|
+
rb_ary_push(values, ULL2NUM(request_args[index]));
|
|
61
|
+
rb_hash_aset(result, ID2SYM(rb_intern("args")), values);
|
|
62
|
+
return rb_ary_new_from_args(2, INT2NUM(0), result);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static VALUE
|
|
66
|
+
seccomp_ext_notify_fd(VALUE self, VALUE object)
|
|
67
|
+
{
|
|
68
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
69
|
+
|
|
70
|
+
(void)self;
|
|
71
|
+
return INT2NUM(seccomp_notify_fd(filter->ctx));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static VALUE
|
|
75
|
+
seccomp_ext_notify_receive(VALUE self, VALUE fd_value)
|
|
76
|
+
{
|
|
77
|
+
struct seccomp_receive_args args = {
|
|
78
|
+
.fd = seccomp_ext_num_to_int(fd_value, "fd"),
|
|
79
|
+
.request = NULL,
|
|
80
|
+
.rc = 0,
|
|
81
|
+
};
|
|
82
|
+
int rc;
|
|
83
|
+
|
|
84
|
+
(void)self;
|
|
85
|
+
rc = seccomp_notify_alloc(&args.request, NULL);
|
|
86
|
+
if (rc < 0)
|
|
87
|
+
return rb_ary_new_from_args(2, INT2NUM(rc), Qnil);
|
|
88
|
+
return rb_ensure(seccomp_ext_notify_receive_call, (VALUE)&args,
|
|
89
|
+
seccomp_ext_notify_receive_free, (VALUE)&args);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static VALUE
|
|
93
|
+
seccomp_ext_notify_respond(VALUE self, VALUE fd_value, VALUE id, VALUE value,
|
|
94
|
+
VALUE error, VALUE flags)
|
|
95
|
+
{
|
|
96
|
+
struct seccomp_notif_resp *response = NULL;
|
|
97
|
+
uint64_t response_id;
|
|
98
|
+
int64_t response_value;
|
|
99
|
+
int response_error;
|
|
100
|
+
uint32_t response_flags;
|
|
101
|
+
int fd;
|
|
102
|
+
int rc;
|
|
103
|
+
|
|
104
|
+
(void)self;
|
|
105
|
+
response_id = seccomp_ext_num_to_uint64(id, "id");
|
|
106
|
+
if (!RB_INTEGER_TYPE_P(value))
|
|
107
|
+
rb_raise(rb_eTypeError, "value must be an Integer");
|
|
108
|
+
response_value = NUM2LL(value);
|
|
109
|
+
response_error = seccomp_ext_num_to_int(error, "error");
|
|
110
|
+
response_flags = seccomp_ext_num_to_uint32(flags, "flags");
|
|
111
|
+
fd = seccomp_ext_num_to_int(fd_value, "fd");
|
|
112
|
+
rc = seccomp_notify_alloc(NULL, &response);
|
|
113
|
+
if (rc < 0)
|
|
114
|
+
return INT2NUM(rc);
|
|
115
|
+
response->id = response_id;
|
|
116
|
+
response->val = response_value;
|
|
117
|
+
response->error = response_error;
|
|
118
|
+
response->flags = response_flags;
|
|
119
|
+
rc = seccomp_notify_respond(fd, response);
|
|
120
|
+
seccomp_notify_free(NULL, response);
|
|
121
|
+
return INT2NUM(rc);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static VALUE
|
|
125
|
+
seccomp_ext_notify_id_valid(VALUE self, VALUE fd_value, VALUE id)
|
|
126
|
+
{
|
|
127
|
+
(void)self;
|
|
128
|
+
return INT2NUM(seccomp_notify_id_valid(seccomp_ext_num_to_int(fd_value, "fd"),
|
|
129
|
+
seccomp_ext_num_to_uint64(id, "id")));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static VALUE
|
|
133
|
+
seccomp_ext_notify_addfd(VALUE self, VALUE fd_value, VALUE id, VALUE flags,
|
|
134
|
+
VALUE source_fd, VALUE new_fd, VALUE new_fd_flags)
|
|
135
|
+
{
|
|
136
|
+
#if defined(HAVE_SECCOMP_NOTIFY_ADDFD) || defined(HAVE_SECCOMP_IOCTL_NOTIF_ADDFD)
|
|
137
|
+
uint32_t target_flags = seccomp_ext_num_to_uint32(new_fd_flags, "new fd flags");
|
|
138
|
+
struct seccomp_notif_addfd addfd = {
|
|
139
|
+
.id = seccomp_ext_num_to_uint64(id, "id"),
|
|
140
|
+
.flags = seccomp_ext_num_to_uint32(flags, "flags"),
|
|
141
|
+
.srcfd = seccomp_ext_num_to_uint32(source_fd, "source fd"),
|
|
142
|
+
.newfd = seccomp_ext_num_to_uint32(new_fd, "new fd"),
|
|
143
|
+
#ifdef HAVE_STRUCT_SECCOMP_NOTIF_ADDFD_NEWFD_FLAGS
|
|
144
|
+
.newfd_flags = target_flags,
|
|
145
|
+
#endif
|
|
146
|
+
};
|
|
147
|
+
int fd = seccomp_ext_num_to_int(fd_value, "fd");
|
|
148
|
+
|
|
149
|
+
(void)self;
|
|
150
|
+
#ifndef HAVE_STRUCT_SECCOMP_NOTIF_ADDFD_NEWFD_FLAGS
|
|
151
|
+
if (target_flags != 0)
|
|
152
|
+
rb_raise(rb_path2class("Seccomp::NotSupportedError"),
|
|
153
|
+
"new fd flags are unavailable with these headers");
|
|
154
|
+
#endif
|
|
155
|
+
#ifdef HAVE_SECCOMP_NOTIFY_ADDFD
|
|
156
|
+
return INT2NUM(seccomp_notify_addfd(fd, &addfd));
|
|
157
|
+
#else
|
|
158
|
+
{
|
|
159
|
+
int rc = ioctl(fd, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd);
|
|
160
|
+
|
|
161
|
+
return INT2NUM(rc < 0 ? -errno : rc);
|
|
162
|
+
}
|
|
163
|
+
#endif
|
|
164
|
+
#else
|
|
165
|
+
(void)self;
|
|
166
|
+
(void)fd_value;
|
|
167
|
+
(void)id;
|
|
168
|
+
(void)flags;
|
|
169
|
+
(void)source_fd;
|
|
170
|
+
(void)new_fd;
|
|
171
|
+
(void)new_fd_flags;
|
|
172
|
+
rb_raise(rb_path2class("Seccomp::NotSupportedError"), "seccomp_notify_addfd unavailable");
|
|
173
|
+
#endif
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
void
|
|
177
|
+
seccomp_ext_init_notify(void)
|
|
178
|
+
{
|
|
179
|
+
rb_define_singleton_method(mSeccompLowLevel, "notify_fd", seccomp_ext_notify_fd, 1);
|
|
180
|
+
rb_define_singleton_method(mSeccompLowLevel, "notify_receive", seccomp_ext_notify_receive, 1);
|
|
181
|
+
rb_define_singleton_method(mSeccompLowLevel, "notify_respond", seccomp_ext_notify_respond, 5);
|
|
182
|
+
rb_define_singleton_method(mSeccompLowLevel, "notify_id_valid", seccomp_ext_notify_id_valid, 2);
|
|
183
|
+
rb_define_singleton_method(mSeccompLowLevel, "notify_addfd", seccomp_ext_notify_addfd, 6);
|
|
184
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#include "seccomp_ext.h"
|
|
2
|
+
|
|
3
|
+
VALUE mSeccomp;
|
|
4
|
+
VALUE mSeccompLowLevel;
|
|
5
|
+
VALUE cSeccompContext;
|
|
6
|
+
VALUE eSeccompClosedFilterError;
|
|
7
|
+
|
|
8
|
+
void
|
|
9
|
+
Init_seccomp_ext(void)
|
|
10
|
+
{
|
|
11
|
+
mSeccomp = rb_define_module("Seccomp");
|
|
12
|
+
mSeccompLowLevel = rb_define_module_under(mSeccomp, "LowLevel");
|
|
13
|
+
eSeccompClosedFilterError = rb_path2class("Seccomp::ClosedFilterError");
|
|
14
|
+
|
|
15
|
+
seccomp_ext_init_util();
|
|
16
|
+
seccomp_ext_init_constants();
|
|
17
|
+
seccomp_ext_init_filter();
|
|
18
|
+
seccomp_ext_init_syscall();
|
|
19
|
+
seccomp_ext_init_notify();
|
|
20
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#ifndef SECCOMP_EXT_H
|
|
2
|
+
#define SECCOMP_EXT_H
|
|
3
|
+
|
|
4
|
+
#include "ruby.h"
|
|
5
|
+
#include <seccomp.h>
|
|
6
|
+
|
|
7
|
+
typedef struct {
|
|
8
|
+
scmp_filter_ctx ctx;
|
|
9
|
+
int loaded;
|
|
10
|
+
int notify_fd;
|
|
11
|
+
int in_transaction;
|
|
12
|
+
int has_notification;
|
|
13
|
+
int notification_used;
|
|
14
|
+
int transaction_has_notification;
|
|
15
|
+
} seccomp_filter_t;
|
|
16
|
+
|
|
17
|
+
extern VALUE mSeccomp;
|
|
18
|
+
extern VALUE mSeccompLowLevel;
|
|
19
|
+
extern VALUE cSeccompContext;
|
|
20
|
+
extern VALUE eSeccompClosedFilterError;
|
|
21
|
+
extern const rb_data_type_t seccomp_filter_type;
|
|
22
|
+
|
|
23
|
+
seccomp_filter_t *seccomp_ext_filter_check(VALUE object);
|
|
24
|
+
VALUE seccomp_ext_uint32(uint32_t value);
|
|
25
|
+
uint32_t seccomp_ext_num_to_uint32(VALUE value, const char *name);
|
|
26
|
+
int seccomp_ext_num_to_int(VALUE value, const char *name);
|
|
27
|
+
uint64_t seccomp_ext_num_to_uint64(VALUE value, const char *name);
|
|
28
|
+
|
|
29
|
+
void seccomp_ext_init_constants(void);
|
|
30
|
+
void seccomp_ext_init_filter(void);
|
|
31
|
+
void seccomp_ext_init_syscall(void);
|
|
32
|
+
void seccomp_ext_init_notify(void);
|
|
33
|
+
void seccomp_ext_init_util(void);
|
|
34
|
+
|
|
35
|
+
#endif
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#include "seccomp_ext.h"
|
|
2
|
+
#include <stdlib.h>
|
|
3
|
+
|
|
4
|
+
static VALUE
|
|
5
|
+
seccomp_ext_syscall_name_to_string(VALUE pointer)
|
|
6
|
+
{
|
|
7
|
+
return rb_str_new_cstr((char *)pointer);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static VALUE
|
|
11
|
+
seccomp_ext_syscall_name_free(VALUE pointer)
|
|
12
|
+
{
|
|
13
|
+
free((char *)pointer);
|
|
14
|
+
return Qnil;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static VALUE
|
|
18
|
+
seccomp_ext_arch_resolve_name(VALUE self, VALUE name)
|
|
19
|
+
{
|
|
20
|
+
(void)self;
|
|
21
|
+
Check_Type(name, T_STRING);
|
|
22
|
+
return seccomp_ext_uint32(seccomp_arch_resolve_name(StringValueCStr(name)));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static VALUE
|
|
26
|
+
seccomp_ext_arch_native(VALUE self)
|
|
27
|
+
{
|
|
28
|
+
(void)self;
|
|
29
|
+
return seccomp_ext_uint32(seccomp_arch_native());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static VALUE
|
|
33
|
+
seccomp_ext_syscall_resolve_name(VALUE self, VALUE name)
|
|
34
|
+
{
|
|
35
|
+
(void)self;
|
|
36
|
+
Check_Type(name, T_STRING);
|
|
37
|
+
return INT2NUM(seccomp_syscall_resolve_name(StringValueCStr(name)));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static VALUE
|
|
41
|
+
seccomp_ext_syscall_resolve_name_arch(VALUE self, VALUE arch, VALUE name)
|
|
42
|
+
{
|
|
43
|
+
(void)self;
|
|
44
|
+
Check_Type(name, T_STRING);
|
|
45
|
+
return INT2NUM(seccomp_syscall_resolve_name_arch(
|
|
46
|
+
seccomp_ext_num_to_uint32(arch, "arch"), StringValueCStr(name)));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static VALUE
|
|
50
|
+
seccomp_ext_syscall_resolve_name_rewrite(VALUE self, VALUE arch, VALUE name)
|
|
51
|
+
{
|
|
52
|
+
(void)self;
|
|
53
|
+
Check_Type(name, T_STRING);
|
|
54
|
+
return INT2NUM(seccomp_syscall_resolve_name_rewrite(
|
|
55
|
+
seccomp_ext_num_to_uint32(arch, "arch"), StringValueCStr(name)));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static VALUE
|
|
59
|
+
seccomp_ext_syscall_resolve_num_arch(VALUE self, VALUE arch, VALUE number)
|
|
60
|
+
{
|
|
61
|
+
char *name;
|
|
62
|
+
|
|
63
|
+
(void)self;
|
|
64
|
+
name = seccomp_syscall_resolve_num_arch(seccomp_ext_num_to_uint32(arch, "arch"),
|
|
65
|
+
seccomp_ext_num_to_int(number, "number"));
|
|
66
|
+
if (name == NULL)
|
|
67
|
+
return Qnil;
|
|
68
|
+
return rb_ensure(seccomp_ext_syscall_name_to_string, (VALUE)name,
|
|
69
|
+
seccomp_ext_syscall_name_free, (VALUE)name);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
void
|
|
73
|
+
seccomp_ext_init_syscall(void)
|
|
74
|
+
{
|
|
75
|
+
rb_define_singleton_method(mSeccompLowLevel, "arch_resolve_name",
|
|
76
|
+
seccomp_ext_arch_resolve_name, 1);
|
|
77
|
+
rb_define_singleton_method(mSeccompLowLevel, "arch_native", seccomp_ext_arch_native, 0);
|
|
78
|
+
rb_define_singleton_method(mSeccompLowLevel, "syscall_resolve_name",
|
|
79
|
+
seccomp_ext_syscall_resolve_name, 1);
|
|
80
|
+
rb_define_singleton_method(mSeccompLowLevel, "syscall_resolve_name_arch",
|
|
81
|
+
seccomp_ext_syscall_resolve_name_arch, 2);
|
|
82
|
+
rb_define_singleton_method(mSeccompLowLevel, "syscall_resolve_name_rewrite",
|
|
83
|
+
seccomp_ext_syscall_resolve_name_rewrite, 2);
|
|
84
|
+
rb_define_singleton_method(mSeccompLowLevel, "syscall_resolve_num_arch",
|
|
85
|
+
seccomp_ext_syscall_resolve_num_arch, 2);
|
|
86
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#include "seccomp_ext.h"
|
|
2
|
+
#include <limits.h>
|
|
3
|
+
|
|
4
|
+
VALUE
|
|
5
|
+
seccomp_ext_uint32(uint32_t value)
|
|
6
|
+
{
|
|
7
|
+
return ULONG2NUM(value);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
uint32_t
|
|
11
|
+
seccomp_ext_num_to_uint32(VALUE value, const char *name)
|
|
12
|
+
{
|
|
13
|
+
unsigned long long number;
|
|
14
|
+
|
|
15
|
+
if (!RB_INTEGER_TYPE_P(value))
|
|
16
|
+
rb_raise(rb_eTypeError, "%s must be an Integer", name);
|
|
17
|
+
number = NUM2ULL(value);
|
|
18
|
+
if (number > UINT32_MAX)
|
|
19
|
+
rb_raise(rb_eRangeError, "%s must fit in uint32", name);
|
|
20
|
+
return (uint32_t)number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
int
|
|
24
|
+
seccomp_ext_num_to_int(VALUE value, const char *name)
|
|
25
|
+
{
|
|
26
|
+
long long number;
|
|
27
|
+
|
|
28
|
+
if (!RB_INTEGER_TYPE_P(value))
|
|
29
|
+
rb_raise(rb_eTypeError, "%s must be an Integer", name);
|
|
30
|
+
number = NUM2LL(value);
|
|
31
|
+
if (number < INT_MIN || number > INT_MAX)
|
|
32
|
+
rb_raise(rb_eRangeError, "%s must fit in int", name);
|
|
33
|
+
return (int)number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
uint64_t
|
|
37
|
+
seccomp_ext_num_to_uint64(VALUE value, const char *name)
|
|
38
|
+
{
|
|
39
|
+
if (!RB_INTEGER_TYPE_P(value))
|
|
40
|
+
rb_raise(rb_eTypeError, "%s must be an Integer", name);
|
|
41
|
+
if (RTEST(rb_funcall(value, rb_intern("<"), 1, INT2FIX(0))))
|
|
42
|
+
rb_raise(rb_eRangeError, "%s must not be negative", name);
|
|
43
|
+
return NUM2ULL(value);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static VALUE
|
|
47
|
+
seccomp_ext_version(VALUE self)
|
|
48
|
+
{
|
|
49
|
+
const struct scmp_version *version = seccomp_version();
|
|
50
|
+
|
|
51
|
+
(void)self;
|
|
52
|
+
return rb_ary_new_from_args(3, UINT2NUM(version->major),
|
|
53
|
+
UINT2NUM(version->minor), UINT2NUM(version->micro));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static VALUE
|
|
57
|
+
seccomp_ext_api_get(VALUE self)
|
|
58
|
+
{
|
|
59
|
+
(void)self;
|
|
60
|
+
return UINT2NUM(seccomp_api_get());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static VALUE
|
|
64
|
+
seccomp_ext_api_set(VALUE self, VALUE level)
|
|
65
|
+
{
|
|
66
|
+
(void)self;
|
|
67
|
+
return INT2NUM(seccomp_api_set(seccomp_ext_num_to_uint32(level, "level")));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static VALUE
|
|
71
|
+
seccomp_ext_supports(VALUE self, VALUE feature)
|
|
72
|
+
{
|
|
73
|
+
ID id;
|
|
74
|
+
|
|
75
|
+
(void)self;
|
|
76
|
+
if (!SYMBOL_P(feature))
|
|
77
|
+
rb_raise(rb_eTypeError, "feature must be a Symbol");
|
|
78
|
+
id = SYM2ID(feature);
|
|
79
|
+
#ifdef HAVE_SECCOMP_PRECOMPUTE
|
|
80
|
+
if (id == rb_intern("precompute")) return Qtrue;
|
|
81
|
+
#endif
|
|
82
|
+
#if defined(HAVE_SECCOMP_NOTIFY_ALLOC)
|
|
83
|
+
if (id == rb_intern("notify")) return Qtrue;
|
|
84
|
+
#endif
|
|
85
|
+
#if defined(HAVE_SECCOMP_NOTIFY_ADDFD) || defined(HAVE_SECCOMP_IOCTL_NOTIF_ADDFD)
|
|
86
|
+
if (id == rb_intern("notify_addfd")) return Qtrue;
|
|
87
|
+
#endif
|
|
88
|
+
#if defined(HAVE_SECCOMP_TRANSACTION_START) && defined(HAVE_SECCOMP_TRANSACTION_COMMIT) && defined(HAVE_SECCOMP_TRANSACTION_REJECT)
|
|
89
|
+
if (id == rb_intern("transaction")) return Qtrue;
|
|
90
|
+
#endif
|
|
91
|
+
#ifdef HAVE_SECCOMP_EXPORT_BPF_MEM
|
|
92
|
+
if (id == rb_intern("bpf_mem")) return Qtrue;
|
|
93
|
+
#endif
|
|
94
|
+
#ifdef HAVE_SCMP_ACT_TRAPX
|
|
95
|
+
if (id == rb_intern("trapx")) return Qtrue;
|
|
96
|
+
#endif
|
|
97
|
+
#ifdef HAVE_CONST_SCMP_FLTATR_CTL_WAITKILL
|
|
98
|
+
if (id == rb_intern("wait_killable")) return Qtrue;
|
|
99
|
+
#endif
|
|
100
|
+
return Qfalse;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
void
|
|
104
|
+
seccomp_ext_init_util(void)
|
|
105
|
+
{
|
|
106
|
+
rb_define_singleton_method(mSeccompLowLevel, "version", seccomp_ext_version, 0);
|
|
107
|
+
rb_define_singleton_method(mSeccompLowLevel, "api_get", seccomp_ext_api_get, 0);
|
|
108
|
+
rb_define_singleton_method(mSeccompLowLevel, "api_set", seccomp_ext_api_set, 1);
|
|
109
|
+
rb_define_singleton_method(mSeccompLowLevel, "supports?", seccomp_ext_supports, 1);
|
|
110
|
+
}
|
data/lib/libseccomp.rb
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "seccomp/version"
|
|
4
|
+
require_relative "seccomp/errors"
|
|
5
|
+
require_relative "seccomp/low_level"
|
|
6
|
+
require "seccomp_ext"
|
|
7
|
+
require_relative "seccomp/action"
|
|
8
|
+
require_relative "seccomp/arch"
|
|
9
|
+
require_relative "seccomp/syscall"
|
|
10
|
+
require_relative "seccomp/arg"
|
|
11
|
+
require_relative "seccomp/attributes"
|
|
12
|
+
require_relative "seccomp/dsl"
|
|
13
|
+
require_relative "seccomp/filter"
|
|
14
|
+
require_relative "seccomp/notification"
|
|
15
|
+
require_relative "seccomp/notifier"
|
|
16
|
+
|
|
17
|
+
## Native bindings and Ruby-friendly APIs for libseccomp.
|
|
18
|
+
module Seccomp
|
|
19
|
+
FEATURE_API_LEVELS = { notify: 5, notify_addfd: 5, wait_killable: 7 }.freeze
|
|
20
|
+
private_constant :FEATURE_API_LEVELS
|
|
21
|
+
|
|
22
|
+
## Semantic version of the linked libseccomp library.
|
|
23
|
+
LibraryVersion = Struct.new(:major, :minor, :micro) do
|
|
24
|
+
# @return [String] dotted version string
|
|
25
|
+
# @example `Seccomp.library_version.to_s #=> "2.6.1"`
|
|
26
|
+
def to_s
|
|
27
|
+
values.join(".")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# @return [LibraryVersion] linked library version
|
|
33
|
+
# @example `Seccomp.library_version.major`
|
|
34
|
+
def library_version
|
|
35
|
+
LibraryVersion.new(*LowLevel.version)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Integer] runtime libseccomp API level
|
|
39
|
+
# @example `Seccomp.api_level #=> 6`
|
|
40
|
+
def api_level
|
|
41
|
+
LowLevel.api_get
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @param level [Integer] process-global API level
|
|
45
|
+
# @return [Integer] assigned level
|
|
46
|
+
# @raise [Error] if libseccomp rejects the level
|
|
47
|
+
# @example `Seccomp.api_level = 6`
|
|
48
|
+
def api_level=(level)
|
|
49
|
+
Error.check!(LowLevel.api_set(level), call: "seccomp_api_set")
|
|
50
|
+
level
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param feature [Symbol] feature name
|
|
54
|
+
# @return [Boolean] whether the linked build provides it
|
|
55
|
+
# @example `Seccomp.supports?(:transaction)`
|
|
56
|
+
def supports?(feature)
|
|
57
|
+
required = FEATURE_API_LEVELS[feature]
|
|
58
|
+
LowLevel.supports?(feature) && (!required || api_level >= required)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @param default [Symbol, Integer] default action
|
|
62
|
+
# @yield [Filter] configured filter for arity-one blocks
|
|
63
|
+
# @return [Filter] configured filter
|
|
64
|
+
# @raise [Error] if configuration fails
|
|
65
|
+
# @example `Seccomp.filter(default: :allow) { deny :ptrace }`
|
|
66
|
+
def filter(default: :kill_process, &block)
|
|
67
|
+
filter = Filter.new(default)
|
|
68
|
+
return filter unless block
|
|
69
|
+
|
|
70
|
+
block.arity.zero? ? filter.instance_eval(&block) : block.call(filter)
|
|
71
|
+
completed = true
|
|
72
|
+
filter
|
|
73
|
+
ensure
|
|
74
|
+
filter&.close if block && !completed
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Seccomp
|
|
4
|
+
# Seccomp action constants and conversions.
|
|
5
|
+
module Action
|
|
6
|
+
# Mask for action payload data.
|
|
7
|
+
# @api private
|
|
8
|
+
DATA_MASK = 0xFFFF
|
|
9
|
+
# Base value for errno actions.
|
|
10
|
+
# @api private
|
|
11
|
+
ERRNO = 0x0005_0000
|
|
12
|
+
# Base value for trace actions.
|
|
13
|
+
# @api private
|
|
14
|
+
TRACE = 0x7FF0_0000
|
|
15
|
+
# Mask selecting an action without its payload.
|
|
16
|
+
# @api private
|
|
17
|
+
ACTION_MASK = 0xFFFF_0000
|
|
18
|
+
# Minimum runtime API level for newer actions.
|
|
19
|
+
# @api private
|
|
20
|
+
API_LEVELS = {
|
|
21
|
+
KILL_PROCESS => 3,
|
|
22
|
+
LOG => 3,
|
|
23
|
+
NOTIFY => 5
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
# Symbol-to-action lookup.
|
|
27
|
+
# @api private
|
|
28
|
+
NAMES = {
|
|
29
|
+
kill_process: KILL_PROCESS,
|
|
30
|
+
kill_thread: KILL_THREAD,
|
|
31
|
+
kill: KILL,
|
|
32
|
+
trap: TRAP,
|
|
33
|
+
notify: NOTIFY,
|
|
34
|
+
log: LOG,
|
|
35
|
+
allow: ALLOW
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
module_function
|
|
39
|
+
|
|
40
|
+
# @param value [Integer, SystemCallError, Class] positive or negative errno
|
|
41
|
+
# @return [Integer] SCMP_ACT_ERRNO action
|
|
42
|
+
# @raise [ArgumentError] for an invalid value
|
|
43
|
+
# @example `Action.errno(Errno::EPERM)`
|
|
44
|
+
def errno(value)
|
|
45
|
+
number = errno_number(value)
|
|
46
|
+
raise ArgumentError, "errno must not be zero" if number.zero?
|
|
47
|
+
|
|
48
|
+
ERRNO | data(number.abs)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @param value [Integer] 16-bit trace payload
|
|
52
|
+
# @return [Integer] SCMP_ACT_TRACE action
|
|
53
|
+
# @raise [ArgumentError] outside 0..65535
|
|
54
|
+
# @example `Action.trace(42)`
|
|
55
|
+
def trace(value)
|
|
56
|
+
TRACE | data(value)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @param value [Integer] optional 16-bit trap payload
|
|
60
|
+
# @return [Integer] SCMP_ACT_TRAP action
|
|
61
|
+
# @raise [NotSupportedError] when parameterized traps are unavailable
|
|
62
|
+
# @example `Action.trap(7)`
|
|
63
|
+
def trap(value = 0)
|
|
64
|
+
value = data(value)
|
|
65
|
+
return TRAP if value.zero?
|
|
66
|
+
raise NotSupportedError, "SCMP_ACT_TRAPX is not available" unless Seccomp.supports?(:trapx)
|
|
67
|
+
|
|
68
|
+
TRAP | value
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @param value [Symbol, Integer, Array] action representation
|
|
72
|
+
# @return [Integer] action constant
|
|
73
|
+
# @raise [ArgumentError] for an unknown action
|
|
74
|
+
# @raise [NotSupportedError] when the runtime API level is too low
|
|
75
|
+
# @example `Action.resolve([:errno, Errno::EPERM])`
|
|
76
|
+
def resolve(value)
|
|
77
|
+
action = if value.is_a?(Integer)
|
|
78
|
+
value
|
|
79
|
+
elsif value.is_a?(Array)
|
|
80
|
+
resolve_tuple(value)
|
|
81
|
+
else
|
|
82
|
+
unless value.respond_to?(:to_sym)
|
|
83
|
+
raise ArgumentError, "unknown action: #{value.inspect}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
NAMES.fetch(value.to_sym) do
|
|
87
|
+
raise ArgumentError, "unknown action: #{value.inspect}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
ensure_supported!(action)
|
|
91
|
+
action
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def ensure_supported!(action)
|
|
95
|
+
if (action & ACTION_MASK) == TRAP && action.anybits?(DATA_MASK) && !Seccomp.supports?(:trapx)
|
|
96
|
+
raise NotSupportedError, "SCMP_ACT_TRAPX is not available"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
required = API_LEVELS.fetch(action & ACTION_MASK, 1)
|
|
100
|
+
return if required == 1 || Seccomp.api_level >= required
|
|
101
|
+
|
|
102
|
+
raise NotSupportedError, "action requires libseccomp API level #{required}"
|
|
103
|
+
end
|
|
104
|
+
private_class_method :ensure_supported!
|
|
105
|
+
|
|
106
|
+
# @param value [Integer] action value
|
|
107
|
+
# @return [String] debug representation
|
|
108
|
+
# @example `Action.describe(Action.errno(1))`
|
|
109
|
+
def describe(value)
|
|
110
|
+
return "SCMP_ACT_ERRNO(#{value & DATA_MASK})" if (value & 0xFFFF_0000) == ERRNO
|
|
111
|
+
return "SCMP_ACT_TRACE(#{value & DATA_MASK})" if (value & 0xFFFF_0000) == TRACE
|
|
112
|
+
|
|
113
|
+
name = NAMES.key(value)
|
|
114
|
+
name ? "SCMP_ACT_#{name.to_s.upcase}" : format("0x%08x", value)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def errno_number(value)
|
|
118
|
+
number = if value.is_a?(Class) && value < SystemCallError
|
|
119
|
+
value.const_get(:Errno, false) if value.const_defined?(:Errno, false)
|
|
120
|
+
elsif value.is_a?(SystemCallError)
|
|
121
|
+
value.errno
|
|
122
|
+
else
|
|
123
|
+
value
|
|
124
|
+
end
|
|
125
|
+
unless number.is_a?(Integer)
|
|
126
|
+
raise ArgumentError, "errno must be an Integer or system error with an errno"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
number
|
|
130
|
+
end
|
|
131
|
+
private_class_method :errno_number
|
|
132
|
+
|
|
133
|
+
def data(value)
|
|
134
|
+
unless value.is_a?(Integer) && (0..DATA_MASK).cover?(value)
|
|
135
|
+
raise ArgumentError, "action data must be between 0 and 65535"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
value
|
|
139
|
+
end
|
|
140
|
+
private_class_method :data
|
|
141
|
+
|
|
142
|
+
def resolve_tuple(tuple)
|
|
143
|
+
raise ArgumentError, "unknown action: #{tuple.inspect}" unless tuple.length == 2
|
|
144
|
+
|
|
145
|
+
type, value = tuple
|
|
146
|
+
return errno(value) if type == :errno
|
|
147
|
+
return trace(value) if type == :trace
|
|
148
|
+
return trap(value) if type == :trap
|
|
149
|
+
|
|
150
|
+
raise ArgumentError, "unknown action: #{tuple.inspect}"
|
|
151
|
+
end
|
|
152
|
+
private_class_method :resolve_tuple
|
|
153
|
+
end
|
|
154
|
+
end
|