]> git.ipfire.org Git - people/ms/pakfire.git/blame - tests/libpakfire/jail.c
Revert "jail: This changes how we launch sub-processes"
[people/ms/pakfire.git] / tests / libpakfire / jail.c
CommitLineData
3334e9c0
MT
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
cc6e2264
MT
21#include <sys/mount.h>
22
6962faab 23#include <pakfire/cgroup.h>
3334e9c0
MT
24#include <pakfire/jail.h>
25
26#include "../testsuite.h"
27
2e664540
MT
28static const char* cmd_hello_world[] = {
29 "/command", "echo", "Hello World!", NULL,
3334e9c0
MT
30};
31
93550e9b
MT
32static const char* cmd_exhaust_memory[] = {
33 "/command", "exhaust-memory", NULL,
34};
35
36static const char* cmd_fork_bomb[] = {
37 "/command", "fork-bomb", NULL,
38};
39
bddfe668
MT
40static const char* cmd_stat_ownership[] = {
41 "/command", "stat-ownership", NULL,
42};
43
3334e9c0
MT
44static int test_create(const struct test* t) {
45 struct pakfire_jail* jail = NULL;
46
47 // Create a new jail
1b55a8bf 48 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
3334e9c0
MT
49
50 // Destroy it
51 ASSERT_NULL(pakfire_jail_unref(jail));
52
53 return EXIT_SUCCESS;
54
55FAIL:
56 return EXIT_FAILURE;
57}
58
39a10052
MT
59static 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
1b55a8bf 68 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
39a10052
MT
69
70 // Check if we receive the correct exit code
1b55a8bf 71 ASSERT(pakfire_jail_exec(jail, argv, NULL, NULL, NULL, 0) == 123);
39a10052
MT
72
73 // Success
74 r = EXIT_SUCCESS;
75
76FAIL:
77 if (jail)
78 pakfire_jail_unref(jail);
79
80 return r;
81}
82
870f7edb
MT
83static 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
1b55a8bf 92 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
870f7edb
MT
93
94 // Check if we receive the correct exit code
1b55a8bf 95 ASSERT(pakfire_jail_exec(jail, argv, NULL, NULL, NULL, 0) == 139);
870f7edb
MT
96
97 // Success
98 r = EXIT_SUCCESS;
99
100FAIL:
101 if (jail)
102 pakfire_jail_unref(jail);
103
104 return r;
105}
106
32d5f21d
MT
107static int test_env(const struct test* t) {
108 struct pakfire_jail* jail = NULL;
109
110 // Create a new jail
1b55a8bf 111 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
32d5f21d 112
d5bc8fe0
MT
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
32d5f21d
MT
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
131FAIL:
132 return EXIT_FAILURE;
133}
134
0bd84dc1
MT
135static int test_exec(const struct test* t) {
136 struct pakfire_jail* jail = NULL;
137
0bd84dc1 138 // Create a new jail
1b55a8bf 139 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
0bd84dc1
MT
140
141 // Try to execute something
1b55a8bf 142 ASSERT_SUCCESS(pakfire_jail_exec(jail, cmd_hello_world, NULL, NULL, NULL, 0));
0bd84dc1
MT
143
144 // Destroy it
145 ASSERT_NULL(pakfire_jail_unref(jail));
146
147 return EXIT_SUCCESS;
148
149FAIL:
150 return EXIT_FAILURE;
151}
32d5f21d 152
6962faab
MT
153static 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
35bf392c 159 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->ctx, "pakfire-test", 0));
6962faab
MT
160
161 // Create a new jail
1b55a8bf 162 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
6962faab
MT
163
164 // Connect jail to the cgroup
165 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
166
167 // Run command
1b55a8bf 168 ASSERT(pakfire_jail_exec(jail, cmd_hello_world, NULL, NULL, NULL, 0) == 0);
6962faab
MT
169
170 r = EXIT_SUCCESS;
171
172FAIL:
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
17841bb8
MT
183static int test_nice(const struct test* t) {
184 struct pakfire_jail* jail = NULL;
12b9b39f 185 char* output = NULL;
17841bb8
MT
186 int r = EXIT_FAILURE;
187
188 const char* argv[] = {
189 "/command", "print-nice", NULL,
190 };
191
17841bb8 192 // Create a new jail
1b55a8bf 193 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
17841bb8
MT
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
12b9b39f 202 // Check if the nice level has been set
ccdd2e95 203 ASSERT_SUCCESS(pakfire_jail_exec(jail, argv,
1b55a8bf 204 NULL, pakfire_jail_capture_stdout, &output, 0));
12b9b39f 205 ASSERT_STRING_EQUALS(output, "5\n");
17841bb8
MT
206
207 // Success
208 r = EXIT_SUCCESS;
209
210FAIL:
211 if (jail)
212 pakfire_jail_unref(jail);
12b9b39f 213 if (output)
17841bb8 214 free(output);
17841bb8
MT
215
216 return r;
217}
218
9bc7ec4b
MT
219static int test_memory_limit(const struct test* t) {
220 struct pakfire_cgroup* cgroup = NULL;
221 struct pakfire_jail* jail = NULL;
222 int r = EXIT_FAILURE;
223
9bc7ec4b
MT
224
225 // Create cgroup
35bf392c 226 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->ctx, "pakfire-test", 0));
9bc7ec4b
MT
227
228 // Create jail
1b55a8bf 229 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
9bc7ec4b
MT
230
231 // Connect jail to the cgroup
232 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
233
234 // Set a memory limit of 100 MiB
235 ASSERT_SUCCESS(pakfire_cgroup_set_memory_limit(cgroup, 100 * 1024 * 1024));
236
237 // Try to exhaust all memory
1b55a8bf 238 ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_exhaust_memory, NULL, NULL, NULL, 0));
93550e9b
MT
239
240 // A fork bomb should also exhaust all memory
1b55a8bf 241 ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_fork_bomb, NULL, NULL, NULL, 0));
9bc7ec4b
MT
242
243 // Success
244 r = EXIT_SUCCESS;
245
246FAIL:
247 if (jail)
248 pakfire_jail_unref(jail);
249 if (cgroup) {
250 pakfire_cgroup_destroy(cgroup);
251 pakfire_cgroup_unref(cgroup);
252 }
253
254 return r;
255}
256
d3b93302
MT
257static int test_pid_limit(const struct test* t) {
258 struct pakfire_cgroup* cgroup = NULL;
259 struct pakfire_jail* jail = NULL;
260 int r = EXIT_FAILURE;
261
d3b93302 262 // Create cgroup
35bf392c 263 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->ctx, "pakfire-test", 0));
d3b93302
MT
264
265 // Create jail
1b55a8bf 266 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
d3b93302
MT
267
268 // Connect jail to the cgroup
269 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
270
271 // Set a PID limit of 100 processes
272 ASSERT_SUCCESS(pakfire_cgroup_set_pid_limit(cgroup, 100));
273
274 // Try to fork as many processes as possible
1b55a8bf 275 ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_fork_bomb, NULL, NULL, NULL, 0));
d3b93302
MT
276
277 // Success
278 r = EXIT_SUCCESS;
279
280FAIL:
281 if (jail)
282 pakfire_jail_unref(jail);
283 if (cgroup) {
284 pakfire_cgroup_destroy(cgroup);
285 pakfire_cgroup_unref(cgroup);
286 }
287
288 return r;
289}
290
bddfe668
MT
291static int test_file_ownership(const struct test* t) {
292 int r = EXIT_FAILURE;
293 char* output = NULL;
294
295 // Execute a simple command
296 ASSERT_SUCCESS(pakfire_jail_run(t->pakfire, cmd_stat_ownership, 0, &output));
297
298 // Check if the file has been mapped to root/root
299 ASSERT_STRING_EQUALS(output, "uid=0 gid=0\n");
300
301 // Success
302 r = EXIT_SUCCESS;
303
304FAIL:
305 if (output)
306 free(output);
307
308 return r;
309}
310
cc6e2264
MT
311static int test_bind(const struct test* t) {
312 struct pakfire_jail* jail = NULL;
313 char* output = NULL;
314 int r = EXIT_FAILURE;
315
316 const char* source = "/";
317 const char* target = "/oldroot";
318
319 const char* argv[] = {
320 "/command", "check-mountpoint", target, NULL,
321 };
322
323 // Create a new jail
1b55a8bf 324 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
cc6e2264
MT
325
326 // Bind-mount nonsense
327 ASSERT_ERRNO(pakfire_jail_bind(jail, NULL, target, 0), EINVAL);
328 ASSERT_ERRNO(pakfire_jail_bind(jail, source, NULL, 0), EINVAL);
329
330 // Bind-mount something
331 ASSERT_SUCCESS(pakfire_jail_bind(jail, source, target, MS_RDONLY));
332
333 // Check if the mount actually works
1b55a8bf 334 ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, NULL, NULL, NULL, 0));
cc6e2264
MT
335
336 // Success
337 r = EXIT_SUCCESS;
338
339FAIL:
340 if (jail)
341 pakfire_jail_unref(jail);
342
343 return r;
344}
345
2015cb92 346static int callback_stdin(struct pakfire* pakfire, void* data, int fd) {
06b864ae 347 int* lines = (int*)data;
2015cb92
MT
348 int r;
349
06b864ae
MT
350 while (*lines > 0) {
351 r = dprintf(fd, "LINE %d\n", *lines);
2015cb92 352 if (r < 0) {
06b864ae 353 LOG_ERROR("Could not write line (%u) to stdin: %m\n", *lines);
2015cb92
MT
354
355 return 1;
356 }
06b864ae
MT
357
358 // Decrement the lines counter
359 (*lines)--;
2015cb92
MT
360 }
361
06b864ae 362 return EOF;
2015cb92
MT
363}
364
365static int test_communicate(const struct test* t) {
366 struct pakfire_jail* jail = NULL;
367 int r = EXIT_FAILURE;
368
06b864ae
MT
369 // How many lines to send?
370 int lines = 65535;
371
2015cb92
MT
372 const char* argv[] = {
373 "/command", "pipe", NULL,
374 };
375
376 // Create a new jail
1b55a8bf 377 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
2015cb92
MT
378
379 // Check if the mount actually works
1b55a8bf 380 ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, callback_stdin, NULL, &lines, 0));
2015cb92
MT
381
382 // Success
383 r = EXIT_SUCCESS;
384
385FAIL:
386 if (jail)
387 pakfire_jail_unref(jail);
388
389 return r;
390}
391
72d37c72
MT
392static int test_send_one_signal(const struct test* t,
393 struct pakfire_jail* jail, const char* signal) {
394 const char* argv[] = {
395 "/command", "send-signal", signal, NULL,
396 };
397
398 // Perform the command
1b55a8bf 399 return pakfire_jail_exec(jail, argv, NULL, NULL, NULL, 0);
72d37c72
MT
400}
401
402static int test_send_signal(const struct test* t) {
403 struct pakfire_jail* jail = NULL;
404 int r = EXIT_FAILURE;
405
406 // Create a new jail
1b55a8bf 407 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
72d37c72
MT
408
409 // Sending SIGTERM to ourselves
410 ASSERT(test_send_one_signal(t, jail, "15") == 0);
411
412 // Sending SIGKILL to ourselves
413 ASSERT(test_send_one_signal(t, jail, "9") == 0);
414
415 // Sending SIGSTOP to ourselves (this should be ignored by the jail)
416 ASSERT(test_send_one_signal(t, jail, "23") == 0);
417
418 // Success
419 r = EXIT_SUCCESS;
420
421FAIL:
422 if (jail)
423 pakfire_jail_unref(jail);
424
425 return r;
426}
427
335b8a44
MT
428static int test_timeout(const struct test* t) {
429 struct pakfire_jail* jail = NULL;
430 int r = EXIT_FAILURE;
431
432 const char* argv[] = {
433 "/command", "sleep", "5", NULL,
434 };
435
436 // Create a new jail
1b55a8bf 437 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
335b8a44
MT
438
439 // Set a timeout of one second
440 ASSERT_SUCCESS(pakfire_jail_set_timeout(jail, 1));
441
442 // Check if we receive the correct exit code
1b55a8bf 443 ASSERT(pakfire_jail_exec(jail, argv, NULL, NULL, NULL, 0) == 139);
335b8a44
MT
444
445 // Success
446 r = EXIT_SUCCESS;
447
448FAIL:
449 if (jail)
450 pakfire_jail_unref(jail);
451
452 return r;
453}
454
b0ead88b 455int main(int argc, const char* argv[]) {
f96ed3ac
MT
456 testsuite_add_test(test_create, TEST_WANTS_PAKFIRE);
457 testsuite_add_test(test_exit_code, TEST_WANTS_PAKFIRE);
458 testsuite_add_test(test_segv, TEST_WANTS_PAKFIRE);
459 testsuite_add_test(test_env, TEST_WANTS_PAKFIRE);
460 testsuite_add_test(test_exec, TEST_WANTS_PAKFIRE);
461 testsuite_add_test(test_launch_into_cgroup, TEST_WANTS_PAKFIRE);
462 testsuite_add_test(test_nice, TEST_WANTS_PAKFIRE);
463 testsuite_add_test(test_memory_limit, TEST_WANTS_PAKFIRE);
464 testsuite_add_test(test_pid_limit, TEST_WANTS_PAKFIRE);
465 testsuite_add_test(test_file_ownership, TEST_WANTS_PAKFIRE);
466 testsuite_add_test(test_bind, TEST_WANTS_PAKFIRE);
467 testsuite_add_test(test_communicate, TEST_WANTS_PAKFIRE);
468 testsuite_add_test(test_send_signal, TEST_WANTS_PAKFIRE);
469 testsuite_add_test(test_timeout, TEST_WANTS_PAKFIRE);
3334e9c0 470
b0ead88b 471 return testsuite_run(argc, argv);
3334e9c0 472}