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,685 @@
|
|
|
1
|
+
#include "seccomp_ext.h"
|
|
2
|
+
#include <errno.h>
|
|
3
|
+
#include <fcntl.h>
|
|
4
|
+
#include <ruby/thread.h>
|
|
5
|
+
#include <unistd.h>
|
|
6
|
+
|
|
7
|
+
struct seccomp_export_args {
|
|
8
|
+
scmp_filter_ctx ctx;
|
|
9
|
+
int fd;
|
|
10
|
+
int rc;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
struct seccomp_bpf_mem_args {
|
|
14
|
+
scmp_filter_ctx ctx;
|
|
15
|
+
void *buffer;
|
|
16
|
+
size_t length;
|
|
17
|
+
size_t capacity;
|
|
18
|
+
int rc;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
struct seccomp_precompute_args {
|
|
22
|
+
scmp_filter_ctx ctx;
|
|
23
|
+
int rc;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
struct seccomp_load_args {
|
|
27
|
+
scmp_filter_ctx ctx;
|
|
28
|
+
int rc;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
static int seccomp_ext_any_filter_loaded;
|
|
32
|
+
|
|
33
|
+
static void *
|
|
34
|
+
seccomp_ext_load_without_gvl(void *pointer)
|
|
35
|
+
{
|
|
36
|
+
struct seccomp_load_args *args = pointer;
|
|
37
|
+
|
|
38
|
+
args->rc = seccomp_load(args->ctx);
|
|
39
|
+
return NULL;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static void *
|
|
43
|
+
seccomp_ext_export_pfc_without_gvl(void *pointer)
|
|
44
|
+
{
|
|
45
|
+
struct seccomp_export_args *args = pointer;
|
|
46
|
+
|
|
47
|
+
args->rc = seccomp_export_pfc(args->ctx, args->fd);
|
|
48
|
+
return NULL;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static void *
|
|
52
|
+
seccomp_ext_export_bpf_without_gvl(void *pointer)
|
|
53
|
+
{
|
|
54
|
+
struct seccomp_export_args *args = pointer;
|
|
55
|
+
|
|
56
|
+
args->rc = seccomp_export_bpf(args->ctx, args->fd);
|
|
57
|
+
return NULL;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#ifdef HAVE_SECCOMP_EXPORT_BPF_MEM
|
|
61
|
+
static void *
|
|
62
|
+
seccomp_ext_export_bpf_mem_without_gvl(void *pointer)
|
|
63
|
+
{
|
|
64
|
+
struct seccomp_bpf_mem_args *args = pointer;
|
|
65
|
+
|
|
66
|
+
args->rc = seccomp_export_bpf_mem(args->ctx, args->buffer, &args->length);
|
|
67
|
+
return NULL;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static VALUE
|
|
71
|
+
seccomp_ext_export_bpf_mem_free(VALUE pointer)
|
|
72
|
+
{
|
|
73
|
+
struct seccomp_bpf_mem_args *args = (struct seccomp_bpf_mem_args *)pointer;
|
|
74
|
+
|
|
75
|
+
xfree(args->buffer);
|
|
76
|
+
args->buffer = NULL;
|
|
77
|
+
return Qnil;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static VALUE
|
|
81
|
+
seccomp_ext_export_bpf_mem_call(VALUE pointer)
|
|
82
|
+
{
|
|
83
|
+
struct seccomp_bpf_mem_args *args = (struct seccomp_bpf_mem_args *)pointer;
|
|
84
|
+
VALUE result;
|
|
85
|
+
|
|
86
|
+
rb_thread_call_without_gvl(seccomp_ext_export_bpf_mem_without_gvl,
|
|
87
|
+
args, RUBY_UBF_IO, NULL);
|
|
88
|
+
if (args->rc < 0)
|
|
89
|
+
return rb_ary_new_from_args(2, INT2NUM(args->rc), Qnil);
|
|
90
|
+
if (args->length > args->capacity)
|
|
91
|
+
return rb_ary_new_from_args(2, INT2NUM(-ERANGE), Qnil);
|
|
92
|
+
result = rb_str_new(args->buffer, args->length);
|
|
93
|
+
return rb_ary_new_from_args(2, INT2NUM(0), result);
|
|
94
|
+
}
|
|
95
|
+
#endif
|
|
96
|
+
|
|
97
|
+
#ifdef HAVE_SECCOMP_PRECOMPUTE
|
|
98
|
+
static void *
|
|
99
|
+
seccomp_ext_precompute_without_gvl(void *pointer)
|
|
100
|
+
{
|
|
101
|
+
struct seccomp_precompute_args *args = pointer;
|
|
102
|
+
|
|
103
|
+
args->rc = seccomp_precompute(args->ctx);
|
|
104
|
+
return NULL;
|
|
105
|
+
}
|
|
106
|
+
#endif
|
|
107
|
+
|
|
108
|
+
static void
|
|
109
|
+
seccomp_ext_filter_close_notify_fd(seccomp_filter_t *filter)
|
|
110
|
+
{
|
|
111
|
+
scmp_filter_ctx probe;
|
|
112
|
+
unsigned int api_level;
|
|
113
|
+
|
|
114
|
+
if (filter->notify_fd < 0)
|
|
115
|
+
return;
|
|
116
|
+
|
|
117
|
+
/* A global reset clears feature flags and the syscall number, but not the API cache. */
|
|
118
|
+
api_level = seccomp_api_get();
|
|
119
|
+
seccomp_reset(NULL, SCMP_ACT_ALLOW);
|
|
120
|
+
/* Reprobe the syscall number before restoring the cached feature flags. */
|
|
121
|
+
probe = seccomp_init(SCMP_ACT_NOTIFY);
|
|
122
|
+
if (probe != NULL) {
|
|
123
|
+
seccomp_release(probe);
|
|
124
|
+
seccomp_api_set(api_level);
|
|
125
|
+
}
|
|
126
|
+
filter->notify_fd = -1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static void
|
|
130
|
+
seccomp_filter_free(void *pointer)
|
|
131
|
+
{
|
|
132
|
+
seccomp_filter_t *filter = pointer;
|
|
133
|
+
|
|
134
|
+
seccomp_ext_filter_close_notify_fd(filter);
|
|
135
|
+
if (filter->ctx != NULL) {
|
|
136
|
+
seccomp_release(filter->ctx);
|
|
137
|
+
filter->ctx = NULL;
|
|
138
|
+
}
|
|
139
|
+
xfree(filter);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static size_t
|
|
143
|
+
seccomp_filter_memsize(const void *pointer)
|
|
144
|
+
{
|
|
145
|
+
return pointer == NULL ? 0 : sizeof(seccomp_filter_t);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const rb_data_type_t seccomp_filter_type = {
|
|
149
|
+
.wrap_struct_name = "Seccomp::LowLevel::Context",
|
|
150
|
+
.function = {
|
|
151
|
+
.dmark = NULL,
|
|
152
|
+
.dfree = seccomp_filter_free,
|
|
153
|
+
.dsize = seccomp_filter_memsize,
|
|
154
|
+
},
|
|
155
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
seccomp_filter_t *
|
|
159
|
+
seccomp_ext_filter_check(VALUE object)
|
|
160
|
+
{
|
|
161
|
+
seccomp_filter_t *filter;
|
|
162
|
+
|
|
163
|
+
TypedData_Get_Struct(object, seccomp_filter_t, &seccomp_filter_type, filter);
|
|
164
|
+
if (filter->ctx == NULL)
|
|
165
|
+
rb_raise(eSeccompClosedFilterError, "filter is closed");
|
|
166
|
+
return filter;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static VALUE
|
|
170
|
+
seccomp_ext_filter_init(VALUE self, VALUE action)
|
|
171
|
+
{
|
|
172
|
+
seccomp_filter_t *filter;
|
|
173
|
+
uint32_t action_value = seccomp_ext_num_to_uint32(action, "action");
|
|
174
|
+
VALUE object;
|
|
175
|
+
|
|
176
|
+
(void)self;
|
|
177
|
+
object = TypedData_Make_Struct(cSeccompContext, seccomp_filter_t,
|
|
178
|
+
&seccomp_filter_type, filter);
|
|
179
|
+
filter->ctx = seccomp_init(action_value);
|
|
180
|
+
filter->loaded = 0;
|
|
181
|
+
filter->notify_fd = -1;
|
|
182
|
+
filter->in_transaction = 0;
|
|
183
|
+
filter->has_notification = action_value == SCMP_ACT_NOTIFY;
|
|
184
|
+
filter->notification_used = filter->has_notification;
|
|
185
|
+
filter->transaction_has_notification = 0;
|
|
186
|
+
if (filter->ctx == NULL)
|
|
187
|
+
rb_raise(rb_path2class("Seccomp::OutOfMemoryError"), "seccomp_init failed");
|
|
188
|
+
return object;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
static VALUE
|
|
192
|
+
seccomp_ext_filter_release(VALUE self, VALUE object)
|
|
193
|
+
{
|
|
194
|
+
seccomp_filter_t *filter;
|
|
195
|
+
|
|
196
|
+
(void)self;
|
|
197
|
+
TypedData_Get_Struct(object, seccomp_filter_t, &seccomp_filter_type, filter);
|
|
198
|
+
seccomp_ext_filter_close_notify_fd(filter);
|
|
199
|
+
if (filter->ctx != NULL) {
|
|
200
|
+
seccomp_release(filter->ctx);
|
|
201
|
+
filter->ctx = NULL;
|
|
202
|
+
}
|
|
203
|
+
return Qnil;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static VALUE
|
|
207
|
+
seccomp_ext_filter_closed(VALUE self, VALUE object)
|
|
208
|
+
{
|
|
209
|
+
seccomp_filter_t *filter;
|
|
210
|
+
|
|
211
|
+
(void)self;
|
|
212
|
+
TypedData_Get_Struct(object, seccomp_filter_t, &seccomp_filter_type, filter);
|
|
213
|
+
return filter->ctx == NULL ? Qtrue : Qfalse;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static VALUE
|
|
217
|
+
seccomp_ext_filter_reset(VALUE self, VALUE object, VALUE action)
|
|
218
|
+
{
|
|
219
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
220
|
+
uint32_t action_value = seccomp_ext_num_to_uint32(action, "action");
|
|
221
|
+
int rc;
|
|
222
|
+
|
|
223
|
+
(void)self;
|
|
224
|
+
rc = seccomp_reset(filter->ctx, action_value);
|
|
225
|
+
if (rc == 0) {
|
|
226
|
+
filter->in_transaction = 0;
|
|
227
|
+
filter->has_notification = action_value == SCMP_ACT_NOTIFY;
|
|
228
|
+
filter->notification_used = filter->has_notification;
|
|
229
|
+
}
|
|
230
|
+
return INT2NUM(rc);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
static int
|
|
234
|
+
seccomp_ext_filter_merge_reverse(scmp_filter_ctx destination, scmp_filter_ctx source)
|
|
235
|
+
{
|
|
236
|
+
/* Preserve destination-only attributes while working around lost NOTIFY state in 2.5/2.6. */
|
|
237
|
+
static const enum scmp_filter_attr attributes[] = {
|
|
238
|
+
SCMP_FLTATR_ACT_BADARCH,
|
|
239
|
+
SCMP_FLTATR_API_TSKIP,
|
|
240
|
+
SCMP_FLTATR_CTL_LOG,
|
|
241
|
+
SCMP_FLTATR_CTL_SSB,
|
|
242
|
+
#ifdef HAVE_CONST_SCMP_FLTATR_CTL_OPTIMIZE
|
|
243
|
+
SCMP_FLTATR_CTL_OPTIMIZE,
|
|
244
|
+
#endif
|
|
245
|
+
#ifdef HAVE_CONST_SCMP_FLTATR_API_SYSRAWRC
|
|
246
|
+
SCMP_FLTATR_API_SYSRAWRC,
|
|
247
|
+
#endif
|
|
248
|
+
#ifdef HAVE_CONST_SCMP_FLTATR_CTL_WAITKILL
|
|
249
|
+
SCMP_FLTATR_CTL_WAITKILL,
|
|
250
|
+
#endif
|
|
251
|
+
};
|
|
252
|
+
uint32_t destination_values[sizeof(attributes) / sizeof(attributes[0])];
|
|
253
|
+
uint32_t source_values[sizeof(attributes) / sizeof(attributes[0])];
|
|
254
|
+
size_t index;
|
|
255
|
+
int merge_rc;
|
|
256
|
+
int rc;
|
|
257
|
+
|
|
258
|
+
for (index = 0; index < sizeof(attributes) / sizeof(attributes[0]); index++) {
|
|
259
|
+
rc = seccomp_attr_get(destination, attributes[index], &destination_values[index]);
|
|
260
|
+
if (rc < 0)
|
|
261
|
+
return rc;
|
|
262
|
+
rc = seccomp_attr_get(source, attributes[index], &source_values[index]);
|
|
263
|
+
if (rc < 0)
|
|
264
|
+
return rc;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
for (index = 0; index < sizeof(attributes) / sizeof(attributes[0]); index++) {
|
|
268
|
+
if (destination_values[index] == source_values[index])
|
|
269
|
+
continue;
|
|
270
|
+
rc = seccomp_attr_set(source, attributes[index], destination_values[index]);
|
|
271
|
+
if (rc < 0)
|
|
272
|
+
goto restore;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
merge_rc = seccomp_merge(source, destination);
|
|
276
|
+
if (merge_rc == 0)
|
|
277
|
+
return 0;
|
|
278
|
+
rc = merge_rc;
|
|
279
|
+
|
|
280
|
+
restore:
|
|
281
|
+
while (index > 0) {
|
|
282
|
+
index--;
|
|
283
|
+
if (destination_values[index] != source_values[index])
|
|
284
|
+
(void)seccomp_attr_set(source, attributes[index], source_values[index]);
|
|
285
|
+
}
|
|
286
|
+
return rc;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
static VALUE
|
|
290
|
+
seccomp_ext_filter_merge(VALUE self, VALUE destination, VALUE source)
|
|
291
|
+
{
|
|
292
|
+
seccomp_filter_t *dst = seccomp_ext_filter_check(destination);
|
|
293
|
+
seccomp_filter_t *src = seccomp_ext_filter_check(source);
|
|
294
|
+
int reverse;
|
|
295
|
+
int rc;
|
|
296
|
+
|
|
297
|
+
(void)self;
|
|
298
|
+
if (dst == src)
|
|
299
|
+
return INT2NUM(-EINVAL);
|
|
300
|
+
if (dst->in_transaction || src->in_transaction)
|
|
301
|
+
return INT2NUM(-EINVAL);
|
|
302
|
+
reverse = !dst->notification_used && src->notification_used;
|
|
303
|
+
rc = reverse ? seccomp_ext_filter_merge_reverse(dst->ctx, src->ctx)
|
|
304
|
+
: seccomp_merge(dst->ctx, src->ctx);
|
|
305
|
+
if (rc == 0) {
|
|
306
|
+
if (reverse)
|
|
307
|
+
dst->ctx = src->ctx;
|
|
308
|
+
dst->loaded = dst->loaded || src->loaded;
|
|
309
|
+
dst->has_notification = dst->has_notification || src->has_notification;
|
|
310
|
+
dst->notification_used = dst->notification_used || src->notification_used;
|
|
311
|
+
if (src->notify_fd >= 0) {
|
|
312
|
+
if (dst->notify_fd < 0)
|
|
313
|
+
dst->notify_fd = src->notify_fd;
|
|
314
|
+
src->notify_fd = -1;
|
|
315
|
+
}
|
|
316
|
+
src->loaded = 0;
|
|
317
|
+
src->in_transaction = 0;
|
|
318
|
+
src->has_notification = 0;
|
|
319
|
+
src->notification_used = 0;
|
|
320
|
+
src->ctx = NULL;
|
|
321
|
+
}
|
|
322
|
+
return INT2NUM(rc);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
static VALUE
|
|
326
|
+
seccomp_ext_filter_attr_get(VALUE self, VALUE object, VALUE attribute)
|
|
327
|
+
{
|
|
328
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
329
|
+
uint32_t value = 0;
|
|
330
|
+
int rc;
|
|
331
|
+
|
|
332
|
+
(void)self;
|
|
333
|
+
rc = seccomp_attr_get(filter->ctx,
|
|
334
|
+
(enum scmp_filter_attr)seccomp_ext_num_to_uint32(attribute, "attribute"),
|
|
335
|
+
&value);
|
|
336
|
+
return rb_ary_new_from_args(2, INT2NUM(rc), seccomp_ext_uint32(value));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
static VALUE
|
|
340
|
+
seccomp_ext_filter_attr_set(VALUE self, VALUE object, VALUE attribute, VALUE value)
|
|
341
|
+
{
|
|
342
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
343
|
+
int rc;
|
|
344
|
+
|
|
345
|
+
(void)self;
|
|
346
|
+
rc = seccomp_attr_set(filter->ctx,
|
|
347
|
+
(enum scmp_filter_attr)seccomp_ext_num_to_uint32(attribute, "attribute"),
|
|
348
|
+
seccomp_ext_num_to_uint32(value, "value"));
|
|
349
|
+
return INT2NUM(rc);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
static VALUE
|
|
353
|
+
seccomp_ext_filter_arch_add(VALUE self, VALUE object, VALUE arch)
|
|
354
|
+
{
|
|
355
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
356
|
+
|
|
357
|
+
(void)self;
|
|
358
|
+
return INT2NUM(seccomp_arch_add(filter->ctx, seccomp_ext_num_to_uint32(arch, "arch")));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
static VALUE
|
|
362
|
+
seccomp_ext_filter_arch_remove(VALUE self, VALUE object, VALUE arch)
|
|
363
|
+
{
|
|
364
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
365
|
+
|
|
366
|
+
(void)self;
|
|
367
|
+
return INT2NUM(seccomp_arch_remove(filter->ctx, seccomp_ext_num_to_uint32(arch, "arch")));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
static VALUE
|
|
371
|
+
seccomp_ext_filter_arch_exist(VALUE self, VALUE object, VALUE arch)
|
|
372
|
+
{
|
|
373
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
374
|
+
|
|
375
|
+
(void)self;
|
|
376
|
+
return INT2NUM(seccomp_arch_exist(filter->ctx, seccomp_ext_num_to_uint32(arch, "arch")));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
static void
|
|
380
|
+
seccomp_ext_filter_comparisons(VALUE comparisons, struct scmp_arg_cmp *result,
|
|
381
|
+
unsigned int *count)
|
|
382
|
+
{
|
|
383
|
+
long length;
|
|
384
|
+
long index;
|
|
385
|
+
|
|
386
|
+
Check_Type(comparisons, T_ARRAY);
|
|
387
|
+
length = RARRAY_LEN(comparisons);
|
|
388
|
+
if (length > 6)
|
|
389
|
+
rb_raise(rb_eArgError, "at most 6 argument comparisons are allowed");
|
|
390
|
+
*count = (unsigned int)length;
|
|
391
|
+
for (index = 0; index < length; index++) {
|
|
392
|
+
VALUE item = rb_ary_entry(comparisons, index);
|
|
393
|
+
uint32_t arg;
|
|
394
|
+
uint32_t op;
|
|
395
|
+
|
|
396
|
+
Check_Type(item, T_ARRAY);
|
|
397
|
+
if (RARRAY_LEN(item) != 4)
|
|
398
|
+
rb_raise(rb_eArgError, "comparison must contain arg, op, datum_a, datum_b");
|
|
399
|
+
arg = seccomp_ext_num_to_uint32(rb_ary_entry(item, 0), "comparison arg");
|
|
400
|
+
op = seccomp_ext_num_to_uint32(rb_ary_entry(item, 1), "comparison op");
|
|
401
|
+
if (arg > 5 || op < SCMP_CMP_NE || op > SCMP_CMP_MASKED_EQ)
|
|
402
|
+
rb_raise(rb_eArgError, "invalid argument comparison");
|
|
403
|
+
result[index].arg = arg;
|
|
404
|
+
result[index].op = (enum scmp_compare)op;
|
|
405
|
+
result[index].datum_a = seccomp_ext_num_to_uint64(rb_ary_entry(item, 2), "datum_a");
|
|
406
|
+
result[index].datum_b = seccomp_ext_num_to_uint64(rb_ary_entry(item, 3), "datum_b");
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
static VALUE
|
|
411
|
+
seccomp_ext_filter_rule_add_common(VALUE object, VALUE action, VALUE syscall,
|
|
412
|
+
VALUE comparisons, int exact)
|
|
413
|
+
{
|
|
414
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
415
|
+
struct scmp_arg_cmp args[6];
|
|
416
|
+
unsigned int count;
|
|
417
|
+
uint32_t action_value = seccomp_ext_num_to_uint32(action, "action");
|
|
418
|
+
int rc;
|
|
419
|
+
|
|
420
|
+
seccomp_ext_filter_comparisons(comparisons, args, &count);
|
|
421
|
+
if (exact) {
|
|
422
|
+
rc = seccomp_rule_add_exact_array(filter->ctx,
|
|
423
|
+
action_value,
|
|
424
|
+
seccomp_ext_num_to_int(syscall, "syscall"), count, args);
|
|
425
|
+
} else {
|
|
426
|
+
rc = seccomp_rule_add_array(filter->ctx,
|
|
427
|
+
action_value,
|
|
428
|
+
seccomp_ext_num_to_int(syscall, "syscall"), count, args);
|
|
429
|
+
}
|
|
430
|
+
if (rc == 0 && action_value == SCMP_ACT_NOTIFY) {
|
|
431
|
+
filter->has_notification = 1;
|
|
432
|
+
filter->notification_used = 1;
|
|
433
|
+
}
|
|
434
|
+
return INT2NUM(rc);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
static VALUE
|
|
438
|
+
seccomp_ext_filter_rule_add_array(VALUE self, VALUE object, VALUE action,
|
|
439
|
+
VALUE syscall, VALUE comparisons)
|
|
440
|
+
{
|
|
441
|
+
(void)self;
|
|
442
|
+
return seccomp_ext_filter_rule_add_common(object, action, syscall, comparisons, 0);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
static VALUE
|
|
446
|
+
seccomp_ext_filter_rule_add_exact_array(VALUE self, VALUE object, VALUE action,
|
|
447
|
+
VALUE syscall, VALUE comparisons)
|
|
448
|
+
{
|
|
449
|
+
(void)self;
|
|
450
|
+
return seccomp_ext_filter_rule_add_common(object, action, syscall, comparisons, 1);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
static VALUE
|
|
454
|
+
seccomp_ext_filter_priority(VALUE self, VALUE object, VALUE syscall, VALUE priority)
|
|
455
|
+
{
|
|
456
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
457
|
+
uint32_t value = seccomp_ext_num_to_uint32(priority, "priority");
|
|
458
|
+
|
|
459
|
+
(void)self;
|
|
460
|
+
if (value > UINT8_MAX)
|
|
461
|
+
rb_raise(rb_eRangeError, "priority must be between 0 and 255");
|
|
462
|
+
return INT2NUM(seccomp_syscall_priority(filter->ctx,
|
|
463
|
+
seccomp_ext_num_to_int(syscall, "syscall"),
|
|
464
|
+
(uint8_t)value));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
static VALUE
|
|
468
|
+
seccomp_ext_filter_export_pfc(VALUE self, VALUE object, VALUE fd)
|
|
469
|
+
{
|
|
470
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
471
|
+
struct seccomp_export_args args = {
|
|
472
|
+
.ctx = filter->ctx,
|
|
473
|
+
.fd = seccomp_ext_num_to_int(fd, "fd"),
|
|
474
|
+
.rc = 0,
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
(void)self;
|
|
478
|
+
rb_thread_call_without_gvl(seccomp_ext_export_pfc_without_gvl, &args, RUBY_UBF_IO, NULL);
|
|
479
|
+
return INT2NUM(args.rc);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
static VALUE
|
|
483
|
+
seccomp_ext_filter_export_bpf(VALUE self, VALUE object, VALUE fd)
|
|
484
|
+
{
|
|
485
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
486
|
+
struct seccomp_export_args args = {
|
|
487
|
+
.ctx = filter->ctx,
|
|
488
|
+
.fd = seccomp_ext_num_to_int(fd, "fd"),
|
|
489
|
+
.rc = 0,
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
(void)self;
|
|
493
|
+
rb_thread_call_without_gvl(seccomp_ext_export_bpf_without_gvl, &args, RUBY_UBF_IO, NULL);
|
|
494
|
+
return INT2NUM(args.rc);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
static VALUE
|
|
498
|
+
seccomp_ext_filter_export_bpf_mem(VALUE self, VALUE object)
|
|
499
|
+
{
|
|
500
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
501
|
+
|
|
502
|
+
#ifdef HAVE_SECCOMP_EXPORT_BPF_MEM
|
|
503
|
+
struct seccomp_bpf_mem_args args = {
|
|
504
|
+
.ctx = filter->ctx,
|
|
505
|
+
.buffer = NULL,
|
|
506
|
+
.length = 0,
|
|
507
|
+
.capacity = 0,
|
|
508
|
+
.rc = 0,
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
(void)self;
|
|
512
|
+
rb_thread_call_without_gvl(seccomp_ext_export_bpf_mem_without_gvl, &args, RUBY_UBF_IO, NULL);
|
|
513
|
+
if (args.rc < 0)
|
|
514
|
+
return rb_ary_new_from_args(2, INT2NUM(args.rc), Qnil);
|
|
515
|
+
args.capacity = args.length;
|
|
516
|
+
args.buffer = xmalloc(args.capacity);
|
|
517
|
+
return rb_ensure(seccomp_ext_export_bpf_mem_call, (VALUE)&args,
|
|
518
|
+
seccomp_ext_export_bpf_mem_free, (VALUE)&args);
|
|
519
|
+
#else
|
|
520
|
+
(void)self;
|
|
521
|
+
(void)filter;
|
|
522
|
+
rb_raise(rb_path2class("Seccomp::NotSupportedError"),
|
|
523
|
+
"seccomp_export_bpf_mem is not available in this libseccomp build");
|
|
524
|
+
#endif
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
static VALUE
|
|
528
|
+
seccomp_ext_filter_precompute(VALUE self, VALUE object)
|
|
529
|
+
{
|
|
530
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
531
|
+
|
|
532
|
+
#ifdef HAVE_SECCOMP_PRECOMPUTE
|
|
533
|
+
struct seccomp_precompute_args args = {.ctx = filter->ctx, .rc = 0};
|
|
534
|
+
|
|
535
|
+
(void)self;
|
|
536
|
+
rb_thread_call_without_gvl(seccomp_ext_precompute_without_gvl, &args, RUBY_UBF_IO, NULL);
|
|
537
|
+
return INT2NUM(args.rc);
|
|
538
|
+
#else
|
|
539
|
+
(void)self;
|
|
540
|
+
(void)filter;
|
|
541
|
+
rb_raise(rb_path2class("Seccomp::NotSupportedError"),
|
|
542
|
+
"seccomp_precompute is not available in this libseccomp build");
|
|
543
|
+
#endif
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
static VALUE
|
|
547
|
+
seccomp_ext_filter_load(VALUE self, VALUE object)
|
|
548
|
+
{
|
|
549
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
550
|
+
struct seccomp_load_args args = {.ctx = filter->ctx, .rc = 0};
|
|
551
|
+
int notify_fd_before;
|
|
552
|
+
int notify_fd_after;
|
|
553
|
+
int reserved_fd = -1;
|
|
554
|
+
|
|
555
|
+
(void)self;
|
|
556
|
+
/* libseccomp retains notification state after rejecting a transaction. */
|
|
557
|
+
if (filter->notification_used && !filter->has_notification)
|
|
558
|
+
return INT2NUM(-EOPNOTSUPP);
|
|
559
|
+
if (filter->notification_used && fcntl(STDIN_FILENO, F_GETFD) < 0 && errno == EBADF)
|
|
560
|
+
reserved_fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
|
561
|
+
notify_fd_before = seccomp_notify_fd(filter->ctx);
|
|
562
|
+
rb_thread_call_without_gvl(seccomp_ext_load_without_gvl, &args, RUBY_UBF_IO, NULL);
|
|
563
|
+
if (args.rc == 0) {
|
|
564
|
+
filter->loaded = 1;
|
|
565
|
+
seccomp_ext_any_filter_loaded = 1;
|
|
566
|
+
notify_fd_after = seccomp_notify_fd(filter->ctx);
|
|
567
|
+
if (notify_fd_after >= 0 && notify_fd_after != notify_fd_before) {
|
|
568
|
+
filter->notify_fd = notify_fd_after;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/* After any successful load, close(2) may already be filtered. */
|
|
572
|
+
if (reserved_fd >= 0 && args.rc < 0 && !seccomp_ext_any_filter_loaded)
|
|
573
|
+
close(reserved_fd);
|
|
574
|
+
return INT2NUM(args.rc);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
static VALUE
|
|
578
|
+
seccomp_ext_filter_loaded(VALUE self, VALUE object)
|
|
579
|
+
{
|
|
580
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
581
|
+
|
|
582
|
+
(void)self;
|
|
583
|
+
return filter->loaded ? Qtrue : Qfalse;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
static VALUE
|
|
587
|
+
seccomp_ext_filter_transaction_start(VALUE self, VALUE object)
|
|
588
|
+
{
|
|
589
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
590
|
+
|
|
591
|
+
#ifdef HAVE_SECCOMP_TRANSACTION_START
|
|
592
|
+
int rc;
|
|
593
|
+
|
|
594
|
+
(void)self;
|
|
595
|
+
rc = seccomp_transaction_start(filter->ctx);
|
|
596
|
+
if (rc == 0) {
|
|
597
|
+
filter->in_transaction = 1;
|
|
598
|
+
filter->transaction_has_notification = filter->has_notification;
|
|
599
|
+
}
|
|
600
|
+
return INT2NUM(rc);
|
|
601
|
+
#else
|
|
602
|
+
(void)self;
|
|
603
|
+
(void)filter;
|
|
604
|
+
rb_raise(rb_path2class("Seccomp::NotSupportedError"), "transactions unavailable");
|
|
605
|
+
#endif
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
static VALUE
|
|
609
|
+
seccomp_ext_filter_transaction_commit(VALUE self, VALUE object)
|
|
610
|
+
{
|
|
611
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
612
|
+
|
|
613
|
+
#ifdef HAVE_SECCOMP_TRANSACTION_COMMIT
|
|
614
|
+
int rc;
|
|
615
|
+
|
|
616
|
+
(void)self;
|
|
617
|
+
rc = seccomp_transaction_commit(filter->ctx);
|
|
618
|
+
if (rc == 0)
|
|
619
|
+
filter->in_transaction = 0;
|
|
620
|
+
return INT2NUM(rc);
|
|
621
|
+
#else
|
|
622
|
+
(void)self;
|
|
623
|
+
(void)filter;
|
|
624
|
+
rb_raise(rb_path2class("Seccomp::NotSupportedError"), "transactions unavailable");
|
|
625
|
+
#endif
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
static VALUE
|
|
629
|
+
seccomp_ext_filter_transaction_reject(VALUE self, VALUE object)
|
|
630
|
+
{
|
|
631
|
+
seccomp_filter_t *filter = seccomp_ext_filter_check(object);
|
|
632
|
+
|
|
633
|
+
#ifdef HAVE_SECCOMP_TRANSACTION_REJECT
|
|
634
|
+
(void)self;
|
|
635
|
+
seccomp_transaction_reject(filter->ctx);
|
|
636
|
+
if (filter->in_transaction)
|
|
637
|
+
filter->has_notification = filter->transaction_has_notification;
|
|
638
|
+
filter->in_transaction = 0;
|
|
639
|
+
return INT2NUM(0);
|
|
640
|
+
#else
|
|
641
|
+
(void)self;
|
|
642
|
+
(void)filter;
|
|
643
|
+
rb_raise(rb_path2class("Seccomp::NotSupportedError"), "transactions unavailable");
|
|
644
|
+
#endif
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
void
|
|
648
|
+
seccomp_ext_init_filter(void)
|
|
649
|
+
{
|
|
650
|
+
cSeccompContext = rb_define_class_under(mSeccompLowLevel, "Context", rb_cObject);
|
|
651
|
+
rb_undef_alloc_func(cSeccompContext);
|
|
652
|
+
|
|
653
|
+
rb_define_singleton_method(mSeccompLowLevel, "init", seccomp_ext_filter_init, 1);
|
|
654
|
+
rb_define_singleton_method(mSeccompLowLevel, "release", seccomp_ext_filter_release, 1);
|
|
655
|
+
rb_define_singleton_method(mSeccompLowLevel, "closed?", seccomp_ext_filter_closed, 1);
|
|
656
|
+
rb_define_singleton_method(mSeccompLowLevel, "reset", seccomp_ext_filter_reset, 2);
|
|
657
|
+
rb_define_singleton_method(mSeccompLowLevel, "merge", seccomp_ext_filter_merge, 2);
|
|
658
|
+
rb_define_singleton_method(mSeccompLowLevel, "attr_get", seccomp_ext_filter_attr_get, 2);
|
|
659
|
+
rb_define_singleton_method(mSeccompLowLevel, "attr_set", seccomp_ext_filter_attr_set, 3);
|
|
660
|
+
rb_define_singleton_method(mSeccompLowLevel, "arch_add", seccomp_ext_filter_arch_add, 2);
|
|
661
|
+
rb_define_singleton_method(mSeccompLowLevel, "arch_remove", seccomp_ext_filter_arch_remove, 2);
|
|
662
|
+
rb_define_singleton_method(mSeccompLowLevel, "arch_exist", seccomp_ext_filter_arch_exist, 2);
|
|
663
|
+
rb_define_singleton_method(mSeccompLowLevel, "rule_add_array",
|
|
664
|
+
seccomp_ext_filter_rule_add_array, 4);
|
|
665
|
+
rb_define_singleton_method(mSeccompLowLevel, "rule_add_exact_array",
|
|
666
|
+
seccomp_ext_filter_rule_add_exact_array, 4);
|
|
667
|
+
rb_define_singleton_method(mSeccompLowLevel, "syscall_priority",
|
|
668
|
+
seccomp_ext_filter_priority, 3);
|
|
669
|
+
rb_define_singleton_method(mSeccompLowLevel, "export_pfc",
|
|
670
|
+
seccomp_ext_filter_export_pfc, 2);
|
|
671
|
+
rb_define_singleton_method(mSeccompLowLevel, "export_bpf",
|
|
672
|
+
seccomp_ext_filter_export_bpf, 2);
|
|
673
|
+
rb_define_singleton_method(mSeccompLowLevel, "export_bpf_mem",
|
|
674
|
+
seccomp_ext_filter_export_bpf_mem, 1);
|
|
675
|
+
rb_define_singleton_method(mSeccompLowLevel, "precompute",
|
|
676
|
+
seccomp_ext_filter_precompute, 1);
|
|
677
|
+
rb_define_singleton_method(mSeccompLowLevel, "load", seccomp_ext_filter_load, 1);
|
|
678
|
+
rb_define_singleton_method(mSeccompLowLevel, "loaded?", seccomp_ext_filter_loaded, 1);
|
|
679
|
+
rb_define_singleton_method(mSeccompLowLevel, "transaction_start",
|
|
680
|
+
seccomp_ext_filter_transaction_start, 1);
|
|
681
|
+
rb_define_singleton_method(mSeccompLowLevel, "transaction_commit",
|
|
682
|
+
seccomp_ext_filter_transaction_commit, 1);
|
|
683
|
+
rb_define_singleton_method(mSeccompLowLevel, "transaction_reject",
|
|
684
|
+
seccomp_ext_filter_transaction_reject, 1);
|
|
685
|
+
}
|