]> git.ipfire.org Git - people/ms/pakfire.git/blob - tests/libpakfire/jail.c
jail: Remove callbacks from command calls
[people/ms/pakfire.git] / tests / libpakfire / jail.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2022 Pakfire development team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <sys/mount.h>
22
23 #include <pakfire/cgroup.h>
24 #include <pakfire/jail.h>
25
26 #include "../testsuite.h"
27
28 static const char* cmd_hello_world[] = {
29 "/command", "echo", "Hello World!", NULL,
30 };
31
32 static const char* cmd_exhaust_memory[] = {
33 "/command", "exhaust-memory", NULL,
34 };
35
36 static const char* cmd_fork_bomb[] = {
37 "/command", "fork-bomb", NULL,
38 };
39
40 static const char* cmd_stat_ownership[] = {
41 "/command", "stat-ownership", NULL,
42 };
43
44 static int test_create(const struct test* t) {
45 struct pakfire_jail* jail = NULL;
46
47 // Create a new jail
48 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
49
50 // Destroy it
51 ASSERT_NULL(pakfire_jail_unref(jail));
52
53 return EXIT_SUCCESS;
54
55 FAIL:
56 return EXIT_FAILURE;
57 }
58
59 static int test_exit_code(const struct test* t) {
60 struct pakfire_jail* jail = NULL;
61 int r = EXIT_FAILURE;
62
63 const char* argv[] = {
64 "/command", "exit-with-code", "123", NULL,
65 };
66
67 // Create a new jail
68 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
69
70 // Check if we receive the correct exit code
71 ASSERT(pakfire_jail_exec(jail, argv, 0) == 123);
72
73 // Success
74 r = EXIT_SUCCESS;
75
76 FAIL:
77 if (jail)
78 pakfire_jail_unref(jail);
79
80 return r;
81 }
82
83 static int test_segv(const struct test* t) {
84 struct pakfire_jail* jail = NULL;
85 int r = EXIT_FAILURE;
86
87 const char* argv[] = {
88 "/command", "segv", NULL,
89 };
90
91 // Create a new jail
92 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
93
94 // Check if we receive the correct exit code
95 ASSERT(pakfire_jail_exec(jail, argv, 0) == 139);
96
97 // Success
98 r = EXIT_SUCCESS;
99
100 FAIL:
101 if (jail)
102 pakfire_jail_unref(jail);
103
104 return r;
105 }
106
107 static int test_env(const struct test* t) {
108 struct pakfire_jail* jail = NULL;
109
110 // Create a new jail
111 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
112
113 // Check if the default variables are set
114 ASSERT(pakfire_jail_get_env(jail, "LANG"));
115 ASSERT(pakfire_jail_get_env(jail, "TERM"));
116
117 // Fetch a non-existing environment variable
118 ASSERT_NULL(pakfire_jail_get_env(jail, "VARIABLE"));
119
120 // Set a variable
121 ASSERT_SUCCESS(pakfire_jail_set_env(jail, "VARIABLE", "VALUE"));
122
123 // Read the value back
124 ASSERT_STRING_EQUALS(pakfire_jail_get_env(jail, "VARIABLE"), "VALUE");
125
126 // Destroy it
127 ASSERT_NULL(pakfire_jail_unref(jail));
128
129 return EXIT_SUCCESS;
130
131 FAIL:
132 return EXIT_FAILURE;
133 }
134
135 static int test_exec(const struct test* t) {
136 struct pakfire_jail* jail = NULL;
137
138 // Create a new jail
139 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
140
141 // Try to execute something
142 ASSERT_SUCCESS(pakfire_jail_exec(jail, cmd_hello_world, 0));
143
144 // Destroy it
145 ASSERT_NULL(pakfire_jail_unref(jail));
146
147 return EXIT_SUCCESS;
148
149 FAIL:
150 return EXIT_FAILURE;
151 }
152
153 static int test_launch_into_cgroup(const struct test* t) {
154 struct pakfire_cgroup* cgroup = NULL;
155 struct pakfire_jail* jail = NULL;
156 int r = EXIT_FAILURE;
157
158 // Create a new cgroup
159 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->ctx, "pakfire-test", 0));
160
161 // Create a new jail
162 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
163
164 // Connect jail to the cgroup
165 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
166
167 // Run command
168 ASSERT(pakfire_jail_exec(jail, cmd_hello_world, 0) == 0);
169
170 r = EXIT_SUCCESS;
171
172 FAIL:
173 if (cgroup) {
174 pakfire_cgroup_destroy(cgroup);
175 pakfire_cgroup_unref(cgroup);
176 }
177 if (jail)
178 pakfire_jail_unref(jail);
179
180 return r;
181 }
182
183 static int test_nice(const struct test* t) {
184 struct pakfire_jail* jail = NULL;
185 char* output = NULL;
186 int r = EXIT_FAILURE;
187
188 const char* argv[] = {
189 "/command", "print-nice", NULL,
190 };
191
192 // Create a new jail
193 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
194
195 // Set invalid nice levels
196 ASSERT_ERRNO(pakfire_jail_nice(jail, 100), EINVAL);
197 ASSERT_ERRNO(pakfire_jail_nice(jail, -100), EINVAL);
198
199 // Set something sane
200 ASSERT_SUCCESS(pakfire_jail_nice(jail, 5));
201
202 // Capture the output of the next execution
203 pakfire_jail_set_stdout_callback(jail, pakfire_jail_capture_stdout, &output);
204
205 // Check if the nice level has been set
206 ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, 0));
207 ASSERT_STRING_EQUALS(output, "5\n");
208
209 // Success
210 r = EXIT_SUCCESS;
211
212 FAIL:
213 if (jail)
214 pakfire_jail_unref(jail);
215 if (output)
216 free(output);
217
218 return r;
219 }
220
221 static int test_memory_limit(const struct test* t) {
222 struct pakfire_cgroup* cgroup = NULL;
223 struct pakfire_jail* jail = NULL;
224 int r = EXIT_FAILURE;
225
226
227 // Create cgroup
228 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->ctx, "pakfire-test", 0));
229
230 // Create jail
231 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
232
233 // Connect jail to the cgroup
234 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
235
236 // Set a memory limit of 100 MiB
237 ASSERT_SUCCESS(pakfire_cgroup_set_memory_limit(cgroup, 100 * 1024 * 1024));
238
239 // Try to exhaust all memory
240 ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_exhaust_memory, 0));
241
242 // A fork bomb should also exhaust all memory
243 ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_fork_bomb, 0));
244
245 // Success
246 r = EXIT_SUCCESS;
247
248 FAIL:
249 if (jail)
250 pakfire_jail_unref(jail);
251 if (cgroup) {
252 pakfire_cgroup_destroy(cgroup);
253 pakfire_cgroup_unref(cgroup);
254 }
255
256 return r;
257 }
258
259 static int test_pid_limit(const struct test* t) {
260 struct pakfire_cgroup* cgroup = NULL;
261 struct pakfire_jail* jail = NULL;
262 int r = EXIT_FAILURE;
263
264 // Create cgroup
265 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->ctx, "pakfire-test", 0));
266
267 // Create jail
268 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
269
270 // Connect jail to the cgroup
271 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
272
273 // Set a PID limit of 100 processes
274 ASSERT_SUCCESS(pakfire_cgroup_set_pid_limit(cgroup, 100));
275
276 // Try to fork as many processes as possible
277 ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_fork_bomb, 0));
278
279 // Success
280 r = EXIT_SUCCESS;
281
282 FAIL:
283 if (jail)
284 pakfire_jail_unref(jail);
285 if (cgroup) {
286 pakfire_cgroup_destroy(cgroup);
287 pakfire_cgroup_unref(cgroup);
288 }
289
290 return r;
291 }
292
293 static int test_file_ownership(const struct test* t) {
294 int r = EXIT_FAILURE;
295 char* output = NULL;
296
297 // Execute a simple command
298 ASSERT_SUCCESS(pakfire_jail_run(t->pakfire, cmd_stat_ownership, 0, &output));
299
300 // Check if the file has been mapped to root/root
301 ASSERT_STRING_EQUALS(output, "uid=0 gid=0\n");
302
303 // Success
304 r = EXIT_SUCCESS;
305
306 FAIL:
307 if (output)
308 free(output);
309
310 return r;
311 }
312
313 static int test_bind(const struct test* t) {
314 struct pakfire_jail* jail = NULL;
315 char* output = NULL;
316 int r = EXIT_FAILURE;
317
318 const char* source = "/";
319 const char* target = "/oldroot";
320
321 const char* argv[] = {
322 "/command", "check-mountpoint", target, NULL,
323 };
324
325 // Create a new jail
326 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
327
328 // Bind-mount nonsense
329 ASSERT_ERRNO(pakfire_jail_bind(jail, NULL, target, 0), EINVAL);
330 ASSERT_ERRNO(pakfire_jail_bind(jail, source, NULL, 0), EINVAL);
331
332 // Bind-mount something
333 ASSERT_SUCCESS(pakfire_jail_bind(jail, source, target, MS_RDONLY));
334
335 // Check if the mount actually works
336 ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, 0));
337
338 // Success
339 r = EXIT_SUCCESS;
340
341 FAIL:
342 if (jail)
343 pakfire_jail_unref(jail);
344
345 return r;
346 }
347
348 static int callback_stdin(struct pakfire_ctx* ctx, struct pakfire_jail* pakfire, void* data, int fd) {
349 int* lines = (int*)data;
350 int r;
351
352 while (*lines > 0) {
353 r = dprintf(fd, "LINE %d\n", *lines);
354 if (r < 0) {
355 switch (errno) {
356 case EAGAIN:
357 return 0;
358
359 default:
360 LOG_ERROR("Could not write line (%u) to stdin: %m\n", *lines);
361 return -errno;
362 }
363 }
364
365 // Decrement the lines counter
366 (*lines)--;
367 }
368
369 return EOF;
370 }
371
372 static int test_communicate(const struct test* t) {
373 struct pakfire_jail* jail = NULL;
374 int r = EXIT_FAILURE;
375
376 // How many lines to send?
377 int lines = 65535;
378
379 const char* argv[] = {
380 "/command", "pipe", NULL,
381 };
382
383 // Create a new jail
384 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
385
386 pakfire_jail_set_stdin_callback(jail, callback_stdin, &lines);
387
388 // Check if the mount actually works
389 ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, 0));
390
391 // Success
392 r = EXIT_SUCCESS;
393
394 FAIL:
395 if (jail)
396 pakfire_jail_unref(jail);
397
398 return r;
399 }
400
401 static int test_send_one_signal(const struct test* t,
402 struct pakfire_jail* jail, const char* signal) {
403 const char* argv[] = {
404 "/command", "send-signal", signal, NULL,
405 };
406
407 // Perform the command
408 return pakfire_jail_exec(jail, argv, 0);
409 }
410
411 static int test_send_signal(const struct test* t) {
412 struct pakfire_jail* jail = NULL;
413 int r = EXIT_FAILURE;
414
415 // Create a new jail
416 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
417
418 // Sending SIGTERM to ourselves
419 ASSERT(test_send_one_signal(t, jail, "15") == 0);
420
421 // Sending SIGKILL to ourselves
422 ASSERT(test_send_one_signal(t, jail, "9") == 0);
423
424 // Sending SIGSTOP to ourselves (this should be ignored by the jail)
425 ASSERT(test_send_one_signal(t, jail, "23") == 0);
426
427 // Success
428 r = EXIT_SUCCESS;
429
430 FAIL:
431 if (jail)
432 pakfire_jail_unref(jail);
433
434 return r;
435 }
436
437 static int test_timeout(const struct test* t) {
438 struct pakfire_jail* jail = NULL;
439 int r = EXIT_FAILURE;
440
441 const char* argv[] = {
442 "/command", "sleep", "5", NULL,
443 };
444
445 // Create a new jail
446 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
447
448 // Set a timeout of one second
449 ASSERT_SUCCESS(pakfire_jail_set_timeout(jail, 1));
450
451 // Check if we receive the correct exit code
452 ASSERT(pakfire_jail_exec(jail, argv, 0) == 139);
453
454 // Success
455 r = EXIT_SUCCESS;
456
457 FAIL:
458 if (jail)
459 pakfire_jail_unref(jail);
460
461 return r;
462 }
463
464 int main(int argc, const char* argv[]) {
465 testsuite_add_test(test_create, TEST_WANTS_PAKFIRE);
466 testsuite_add_test(test_exit_code, TEST_WANTS_PAKFIRE);
467 testsuite_add_test(test_segv, TEST_WANTS_PAKFIRE);
468 testsuite_add_test(test_env, TEST_WANTS_PAKFIRE);
469 testsuite_add_test(test_exec, TEST_WANTS_PAKFIRE);
470 testsuite_add_test(test_launch_into_cgroup, TEST_WANTS_PAKFIRE);
471 testsuite_add_test(test_nice, TEST_WANTS_PAKFIRE);
472 testsuite_add_test(test_memory_limit, TEST_WANTS_PAKFIRE);
473 testsuite_add_test(test_pid_limit, TEST_WANTS_PAKFIRE);
474 #if 0
475 testsuite_add_test(test_file_ownership, TEST_WANTS_PAKFIRE);
476 #endif
477 testsuite_add_test(test_bind, TEST_WANTS_PAKFIRE);
478 testsuite_add_test(test_communicate, TEST_WANTS_PAKFIRE);
479 testsuite_add_test(test_send_signal, TEST_WANTS_PAKFIRE);
480 testsuite_add_test(test_timeout, TEST_WANTS_PAKFIRE);
481
482 return testsuite_run(argc, argv);
483 }