]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/jail.c
jail: Bring back the UNIX domain socket between child/parent
[pakfire.git] / src / libpakfire / jail.c
CommitLineData
fd37ccaf
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
32d5f21d 21#include <errno.h>
bcf09bf5 22#include <fcntl.h>
980b15af 23#include <linux/capability.h>
0bd84dc1 24#include <linux/sched.h>
58ee649f 25#include <sys/wait.h>
4f23b498 26#include <linux/wait.h>
0bd84dc1
MT
27#include <sched.h>
28#include <signal.h>
32d5f21d 29#include <stdlib.h>
0bd84dc1 30#include <syscall.h>
980b15af 31#include <sys/capability.h>
616f1fca 32#include <sys/epoll.h>
43dc0e16 33#include <sys/eventfd.h>
7bdf1d8e 34#include <sys/mount.h>
90d92b5c 35#include <sys/personality.h>
980b15af 36#include <sys/prctl.h>
cf440db8 37#include <sys/resource.h>
335b8a44 38#include <sys/timerfd.h>
0bd84dc1
MT
39#include <sys/types.h>
40#include <sys/wait.h>
32d5f21d 41
fec79a33
MT
42// libnl3
43#include <net/if.h>
44#include <netlink/route/link.h>
45
739d5b57
MT
46// libseccomp
47#include <seccomp.h>
48
ae5201c5
MT
49// libuuid
50#include <uuid.h>
51
90d92b5c 52#include <pakfire/arch.h>
e3ddb498 53#include <pakfire/cgroup.h>
fd37ccaf 54#include <pakfire/jail.h>
4f59c39b
MT
55#include <pakfire/logging.h>
56#include <pakfire/mount.h>
fd37ccaf 57#include <pakfire/pakfire.h>
729827f7 58#include <pakfire/path.h>
6ce56f90 59#include <pakfire/private.h>
4896e62c 60#include <pakfire/pwd.h>
d973a13d 61#include <pakfire/string.h>
32d5f21d
MT
62#include <pakfire/util.h>
63
616f1fca
MT
64#define BUFFER_SIZE 1024 * 64
65#define ENVIRON_SIZE 128
66#define EPOLL_MAX_EVENTS 2
cc6e2264 67#define MAX_MOUNTPOINTS 8
fd37ccaf 68
d5bc8fe0
MT
69// The default environment that will be set for every command
70static const struct environ {
71 const char* key;
72 const char* val;
73} ENV[] = {
fbe95cdf 74 { "HOME", "/root" },
55f54757 75 { "LANG", "C.utf-8" },
a7ad6d4f 76 { "PATH", "/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin", },
d5bc8fe0 77 { "TERM", "vt100" },
3bf01105
MT
78
79 // Tell everything that it is running inside a Pakfire container
80 { "container", "pakfire" },
d5bc8fe0
MT
81 { NULL, NULL },
82};
83
cc6e2264
MT
84struct pakfire_jail_mountpoint {
85 char source[PATH_MAX];
86 char target[PATH_MAX];
87 int flags;
88};
89
fd37ccaf 90struct pakfire_jail {
a13df023 91 struct pakfire_ctx* ctx;
fd37ccaf
MT
92 struct pakfire* pakfire;
93 int nrefs;
32d5f21d 94
ae5201c5
MT
95 // A unique ID for each jail
96 uuid_t uuid;
97 char __uuid[UUID_STR_LEN];
98
cf440db8
MT
99 // Resource Limits
100 int nice;
101
335b8a44
MT
102 // Timeout
103 struct itimerspec timeout;
104
15503538
MT
105 // CGroup
106 struct pakfire_cgroup* cgroup;
107
32d5f21d
MT
108 // Environment
109 char* env[ENVIRON_SIZE];
616f1fca 110
cc6e2264
MT
111 // Mountpoints
112 struct pakfire_jail_mountpoint mountpoints[MAX_MOUNTPOINTS];
113 unsigned int num_mountpoints;
2dc104da
MT
114
115 // Callbacks
116 struct pakfire_jail_callbacks {
117 // Log
118 pakfire_jail_log_callback log;
119 void* log_data;
120 } callbacks;
616f1fca
MT
121};
122
123struct pakfire_log_buffer {
124 char data[BUFFER_SIZE];
125 size_t used;
126};
127
128struct pakfire_jail_exec {
7bdf1d8e
MT
129 int flags;
130
f949feac
MT
131 // PID (of the child)
132 pid_t pid;
133 int pidfd;
616f1fca 134
8f49d254
MT
135 // Socket to pass FDs
136 int socket[2];
137
f949feac
MT
138 // Process status (from waitid)
139 siginfo_t status;
616f1fca 140
f7d240a7
MT
141 // FD to notify the client that the parent has finished initialization
142 int completed_fd;
143
616f1fca 144 // Log pipes
e33387d3 145 struct pakfire_jail_pipes {
2015cb92 146 int stdin[2];
616f1fca
MT
147 int stdout[2];
148 int stderr[2];
e33387d3
MT
149
150 // Logging
151 int log_INFO[2];
152 int log_ERROR[2];
6ac51607 153#ifdef ENABLE_DEBUG
e33387d3 154 int log_DEBUG[2];
6ac51607 155#endif /* ENABLE_DEBUG */
616f1fca
MT
156 } pipes;
157
2015cb92
MT
158 // Communicate
159 struct pakfire_jail_communicate {
160 pakfire_jail_communicate_in in;
161 pakfire_jail_communicate_out out;
162 void* data;
163 } communicate;
164
616f1fca 165 // Log buffers
e33387d3 166 struct pakfire_jail_buffers {
616f1fca
MT
167 struct pakfire_log_buffer stdout;
168 struct pakfire_log_buffer stderr;
e33387d3
MT
169
170 // Logging
171 struct pakfire_log_buffer log_INFO;
172 struct pakfire_log_buffer log_ERROR;
6ac51607 173#ifdef ENABLE_DEBUG
e33387d3 174 struct pakfire_log_buffer log_DEBUG;
6ac51607 175#endif /* ENABLE_DEBUG */
616f1fca 176 } buffers;
aca565fc
MT
177
178 struct pakfire_cgroup* cgroup;
6b7cf275 179 struct pakfire_cgroup_stats cgroup_stats;
374e6268
MT
180
181 // Console
182 char console[PATH_MAX];
183 int consolefd;
fd37ccaf
MT
184};
185
0bd84dc1
MT
186static int clone3(struct clone_args* args, size_t size) {
187 return syscall(__NR_clone3, args, size);
188}
189
335b8a44
MT
190static int pidfd_send_signal(int pidfd, int sig, siginfo_t* info, unsigned int flags) {
191 return syscall(SYS_pidfd_send_signal, pidfd, sig, info, flags);
192}
193
14df7388
MT
194static int pivot_root(const char* new_root, const char* old_root) {
195 return syscall(SYS_pivot_root, new_root, old_root);
196}
197
7bdf1d8e
MT
198static int pakfire_jail_exec_has_flag(
199 const struct pakfire_jail_exec* ctx, const enum pakfire_jail_exec_flags flag) {
200 return ctx->flags & flag;
201}
202
d5bc8fe0
MT
203static void pakfire_jail_free(struct pakfire_jail* jail) {
204 DEBUG(jail->pakfire, "Freeing jail at %p\n", jail);
205
206 // Free environment
207 for (unsigned int i = 0; jail->env[i]; i++)
208 free(jail->env[i]);
209
d34b1e00
MT
210 if (jail->cgroup)
211 pakfire_cgroup_unref(jail->cgroup);
a13df023
MT
212 if (jail->pakfire)
213 pakfire_unref(jail->pakfire);
214 if (jail->ctx)
215 pakfire_ctx_unref(jail->ctx);
d5bc8fe0
MT
216 free(jail);
217}
218
e33387d3
MT
219/*
220 Passes any log messages on to the default pakfire log callback
221*/
fed41508
MT
222static int pakfire_jail_default_log_callback(struct pakfire* pakfire, void* data,
223 int priority, const char* line, size_t length) {
224 switch (priority) {
225 case LOG_INFO:
226 INFO(pakfire, "%s", line);
227 break;
228
229 case LOG_ERR:
230 ERROR(pakfire, "%s", line);
231 break;
e33387d3
MT
232
233#ifdef ENABLE_DEBUG
234 case LOG_DEBUG:
235 DEBUG(pakfire, "%s", line);
236 break;
237#endif
fed41508
MT
238 }
239
240 return 0;
241}
242
3bf01105
MT
243static const char* pakfire_jail_uuid(struct pakfire_jail* jail) {
244 if (!*jail->__uuid)
245 uuid_unparse_lower(jail->uuid, jail->__uuid);
246
247 return jail->__uuid;
248}
249
00ba1d9a
MT
250static int pakfire_jail_setup_interactive_env(struct pakfire_jail* jail) {
251 // Set PS1
252 int r = pakfire_jail_set_env(jail, "PS1", "pakfire-jail \\w> ");
253 if (r)
254 return r;
255
256 // Copy TERM
257 char* TERM = secure_getenv("TERM");
258 if (TERM) {
259 r = pakfire_jail_set_env(jail, "TERM", TERM);
260 if (r)
261 return r;
262 }
263
264 // Copy LANG
265 char* LANG = secure_getenv("LANG");
266 if (LANG) {
267 r = pakfire_jail_set_env(jail, "LANG", LANG);
268 if (r)
269 return r;
270 }
271
272 return 0;
273}
274
9fa1afb6 275PAKFIRE_EXPORT int pakfire_jail_create(struct pakfire_jail** jail, struct pakfire* pakfire) {
d5bc8fe0
MT
276 int r;
277
652f2a99 278 const char* arch = pakfire_get_effective_arch(pakfire);
aac86bd3 279
d5bc8fe0 280 // Allocate a new jail
fd37ccaf
MT
281 struct pakfire_jail* j = calloc(1, sizeof(*j));
282 if (!j)
283 return 1;
284
a13df023
MT
285 // Reference context
286 j->ctx = pakfire_ctx(pakfire);
287
fd37ccaf
MT
288 // Reference Pakfire
289 j->pakfire = pakfire_ref(pakfire);
290
291 // Initialize reference counter
292 j->nrefs = 1;
293
ae5201c5
MT
294 // Generate a random UUID
295 uuid_generate_random(j->uuid);
296
84bd7655
MT
297 DEBUG(j->pakfire, "Allocated new jail at %p\n", j);
298
2dc104da
MT
299 // Set the default logging callback
300 pakfire_jail_set_log_callback(j, pakfire_jail_default_log_callback, NULL);
301
d5bc8fe0
MT
302 // Set default environment
303 for (const struct environ* e = ENV; e->key; e++) {
304 r = pakfire_jail_set_env(j, e->key, e->val);
305 if (r)
306 goto ERROR;
307 }
308
aac86bd3 309 // Enable all CPU features that CPU has to offer
1f4e66a4 310 if (!pakfire_arch_is_supported_by_host(arch)) {
aac86bd3
MT
311 r = pakfire_jail_set_env(j, "QEMU_CPU", "max");
312 if (r)
313 goto ERROR;
314 }
315
3bf01105
MT
316 // Set container UUID
317 r = pakfire_jail_set_env(j, "container_uuid", pakfire_jail_uuid(j));
318 if (r)
319 goto ERROR;
320
367e708d
MT
321 // Disable systemctl to talk to systemd
322 if (!pakfire_on_root(j->pakfire)) {
323 r = pakfire_jail_set_env(j, "SYSTEMD_OFFLINE", "1");
324 if (r)
325 goto ERROR;
326 }
327
fd37ccaf
MT
328 // Done
329 *jail = j;
330 return 0;
84bd7655 331
d5bc8fe0
MT
332ERROR:
333 pakfire_jail_free(j);
32d5f21d 334
d5bc8fe0 335 return r;
fd37ccaf
MT
336}
337
6ce56f90 338PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_ref(struct pakfire_jail* jail) {
fd37ccaf
MT
339 ++jail->nrefs;
340
341 return jail;
342}
343
6ce56f90 344PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_unref(struct pakfire_jail* jail) {
fd37ccaf
MT
345 if (--jail->nrefs > 0)
346 return jail;
347
348 pakfire_jail_free(jail);
349 return NULL;
350}
32d5f21d 351
2dc104da
MT
352// Logging Callback
353
354PAKFIRE_EXPORT void pakfire_jail_set_log_callback(struct pakfire_jail* jail,
355 pakfire_jail_log_callback callback, void* data) {
356 jail->callbacks.log = callback;
357 jail->callbacks.log_data = data;
358}
359
cf440db8
MT
360// Resource Limits
361
362PAKFIRE_EXPORT int pakfire_jail_nice(struct pakfire_jail* jail, int nice) {
363 // Check if nice level is in range
364 if (nice < -19 || nice > 20) {
365 errno = EINVAL;
366 return 1;
367 }
368
369 // Store nice level
370 jail->nice = nice;
371
372 return 0;
373}
374
15503538
MT
375int pakfire_jail_set_cgroup(struct pakfire_jail* jail, struct pakfire_cgroup* cgroup) {
376 // Free any previous cgroup
377 if (jail->cgroup) {
378 pakfire_cgroup_unref(jail->cgroup);
379 jail->cgroup = NULL;
380 }
381
382 // Set any new cgroup
383 if (cgroup) {
384 DEBUG(jail->pakfire, "Setting cgroup %p\n", cgroup);
385
386 jail->cgroup = pakfire_cgroup_ref(cgroup);
387 }
388
389 // Done
390 return 0;
391}
392
32d5f21d
MT
393// Environment
394
395// Returns the length of the environment
396static unsigned int pakfire_jail_env_length(struct pakfire_jail* jail) {
397 unsigned int i = 0;
398
399 // Count everything in the environment
400 for (char** e = jail->env; *e; e++)
401 i++;
402
403 return i;
404}
405
406// Finds an existing environment variable and returns its index or -1 if not found
407static int pakfire_jail_find_env(struct pakfire_jail* jail, const char* key) {
408 if (!key) {
409 errno = EINVAL;
410 return -1;
411 }
412
b88ae806 413 const size_t length = strlen(key);
32d5f21d
MT
414
415 for (unsigned int i = 0; jail->env[i]; i++) {
b88ae806
MT
416 if ((pakfire_string_startswith(jail->env[i], key)
417 && *(jail->env[i] + length) == '=')) {
32d5f21d 418 return i;
b88ae806 419 }
32d5f21d
MT
420 }
421
422 // Nothing found
423 return -1;
424}
425
426// Returns the value of an environment variable or NULL
6ce56f90
MT
427PAKFIRE_EXPORT const char* pakfire_jail_get_env(struct pakfire_jail* jail,
428 const char* key) {
32d5f21d
MT
429 int i = pakfire_jail_find_env(jail, key);
430 if (i < 0)
431 return NULL;
432
433 return jail->env[i] + strlen(key) + 1;
434}
435
436// Sets an environment variable
6ce56f90
MT
437PAKFIRE_EXPORT int pakfire_jail_set_env(struct pakfire_jail* jail,
438 const char* key, const char* value) {
32d5f21d
MT
439 // Find the index where to write this value to
440 int i = pakfire_jail_find_env(jail, key);
441 if (i < 0)
442 i = pakfire_jail_env_length(jail);
443
444 // Return -ENOSPC when the environment is full
445 if (i >= ENVIRON_SIZE) {
446 errno = ENOSPC;
447 return -1;
448 }
449
450 // Free any previous value
451 if (jail->env[i])
452 free(jail->env[i]);
453
454 // Format and set environment variable
455 asprintf(&jail->env[i], "%s=%s", key, value);
456
457 DEBUG(jail->pakfire, "Set environment variable: %s\n", jail->env[i]);
458
459 return 0;
460}
9f50bf71 461
939025e7 462// Imports an environment
6ce56f90 463PAKFIRE_EXPORT int pakfire_jail_import_env(struct pakfire_jail* jail, const char* env[]) {
939025e7
MT
464 if (!env)
465 return 0;
466
467 char* key;
468 char* val;
469 int r;
470
471 // Copy environment variables
472 for (unsigned int i = 0; env[i]; i++) {
473 r = pakfire_string_partition(env[i], "=", &key, &val);
474 if (r)
475 continue;
476
477 // Set value
478 r = pakfire_jail_set_env(jail, key, val);
479
480 if (key)
481 free(key);
482 if (val)
483 free(val);
484
485 // Break on error
486 if (r)
487 return r;
488 }
489
490 return 0;
491}
492
335b8a44
MT
493// Timeout
494
495PAKFIRE_EXPORT int pakfire_jail_set_timeout(
496 struct pakfire_jail* jail, unsigned int timeout) {
497 // Store value
498 jail->timeout.it_value.tv_sec = timeout;
499
500 if (timeout > 0)
a8a41064 501 DEBUG(jail->pakfire, "Timeout set to %u second(s)\n", timeout);
335b8a44
MT
502 else
503 DEBUG(jail->pakfire, "Timeout disabled\n");
504
505 return 0;
506}
507
508static int pakfire_jail_create_timer(struct pakfire_jail* jail) {
509 int r;
510
511 // Nothing to do if no timeout has been set
512 if (!jail->timeout.it_value.tv_sec)
513 return -1;
514
515 // Create a new timer
516 const int fd = timerfd_create(CLOCK_MONOTONIC, 0);
517 if (fd < 0) {
518 ERROR(jail->pakfire, "Could not create timer: %m\n");
519 goto ERROR;
520 }
521
522 // Arm timer
523 r = timerfd_settime(fd, 0, &jail->timeout, NULL);
524 if (r) {
525 ERROR(jail->pakfire, "Could not arm timer: %m\n");
526 goto ERROR;
527 }
528
529 return fd;
530
531ERROR:
dd2d7dbb 532 if (fd >= 0)
335b8a44
MT
533 close(fd);
534
535 return -1;
536}
537
e33387d3
MT
538/*
539 This function replaces any logging in the child process.
540
541 All log messages will be sent to the parent process through their respective pipes.
542*/
19e50d86 543static void pakfire_jail_log_redirect(void* data, int priority, const char* file,
e33387d3
MT
544 int line, const char* fn, const char* format, va_list args) {
545 struct pakfire_jail_pipes* pipes = (struct pakfire_jail_pipes*)data;
546 int fd;
547
548 switch (priority) {
549 case LOG_INFO:
550 fd = pipes->log_INFO[1];
551 break;
552
553 case LOG_ERR:
554 fd = pipes->log_ERROR[1];
555 break;
556
557#ifdef ENABLE_DEBUG
558 case LOG_DEBUG:
559 fd = pipes->log_DEBUG[1];
560 break;
561#endif /* ENABLE_DEBUG */
562
563 // Ignore any messages of an unknown priority
564 default:
565 return;
566 }
567
568 // Send the log message
4c3bab92 569 if (fd >= 0)
e33387d3
MT
570 vdprintf(fd, format, args);
571}
572
616f1fca
MT
573static int pakfire_jail_log_buffer_is_full(const struct pakfire_log_buffer* buffer) {
574 return (sizeof(buffer->data) == buffer->used);
575}
576
577/*
578 This function reads as much data as it can from the file descriptor.
579 If it finds a whole line in it, it will send it to the logger and repeat the process.
580 If not newline character is found, it will try to read more data until it finds one.
581*/
582static int pakfire_jail_handle_log(struct pakfire_jail* jail,
e33387d3 583 struct pakfire_jail_exec* ctx, int priority, int fd,
2015cb92 584 struct pakfire_log_buffer* buffer, pakfire_jail_communicate_out callback, void* data) {
616f1fca
MT
585 char line[BUFFER_SIZE + 1];
586
587 // Fill up buffer from fd
588 if (buffer->used < sizeof(buffer->data)) {
589 ssize_t bytes_read = read(fd, buffer->data + buffer->used,
590 sizeof(buffer->data) - buffer->used);
591
592 // Handle errors
593 if (bytes_read < 0) {
594 ERROR(jail->pakfire, "Could not read from fd %d: %m\n", fd);
595 return -1;
596 }
597
598 // Update buffer size
599 buffer->used += bytes_read;
600 }
601
602 // See if we have any lines that we can write
603 while (buffer->used) {
604 // Search for the end of the first line
605 char* eol = memchr(buffer->data, '\n', buffer->used);
606
607 // No newline found
608 if (!eol) {
609 // If the buffer is full, we send the content to the logger and try again
610 // This should not happen in practise
611 if (pakfire_jail_log_buffer_is_full(buffer)) {
612 DEBUG(jail->pakfire, "Logging buffer is full. Sending all content\n");
613
614 eol = buffer->data + sizeof(buffer->data) - 1;
615
616 // Otherwise we might have only read parts of the output
617 } else
618 break;
619 }
620
621 // Find the length of the string
622 size_t length = eol - buffer->data + 1;
623
624 // Copy the line into the buffer
625 memcpy(line, buffer->data, length);
626
627 // Terminate the string
628 line[length] = '\0';
629
630 // Log the line
e33387d3
MT
631 if (callback) {
632 int r = callback(jail->pakfire, data, priority, line, length);
616f1fca
MT
633 if (r) {
634 ERROR(jail->pakfire, "The logging callback returned an error: %d\n", r);
635 return r;
636 }
637 }
638
639 // Remove line from buffer
640 memmove(buffer->data, buffer->data + length, buffer->used - length);
641 buffer->used -= length;
642 }
643
644 return 0;
645}
646
06b864ae
MT
647static int pakfire_jail_stream_stdin(struct pakfire_jail* jail,
648 struct pakfire_jail_exec* ctx, const int fd) {
649 int r;
650
651 // Nothing to do if there is no stdin callback set
652 if (!ctx->communicate.in) {
653 DEBUG(jail->pakfire, "Callback for standard input is not set\n");
654 return 0;
655 }
656
f5a70a96
MT
657 // Skip if the writing pipe has already been closed
658 if (!ctx->pipes.stdin[1])
659 return 0;
660
06b864ae
MT
661 DEBUG(jail->pakfire, "Streaming standard input...\n");
662
663 // Calling the callback
664 r = ctx->communicate.in(jail->pakfire, ctx->communicate.data, fd);
665
666 DEBUG(jail->pakfire, "Standard input callback finished: %d\n", r);
667
668 // The callback signaled that it has written everything
669 if (r == EOF) {
670 DEBUG(jail->pakfire, "Closing standard input pipe\n");
671
f5a70a96 672 // Close the file-descriptor
06b864ae 673 close(fd);
f5a70a96
MT
674
675 // Reset the file-descriptor so it won't be closed again later
dd2d7dbb 676 ctx->pipes.stdin[1] = -1;
f5a70a96
MT
677
678 // Report success
06b864ae
MT
679 r = 0;
680 }
681
682 return r;
683}
684
8f49d254
MT
685static int pakfire_jail_recv_fd(struct pakfire_jail* jail, int socket, int* fd) {
686 const size_t payload_length = sizeof(fd);
687 char buffer[CMSG_SPACE(payload_length)];
688 int r;
689
690 struct msghdr msg = {
691 .msg_control = buffer,
692 .msg_controllen = sizeof(buffer),
693 };
694
695 // Receive the message
696 r = recvmsg(socket, &msg, 0);
697 if (r) {
698 CTX_ERROR(jail->ctx, "Could not receive file descriptor: %s\n", strerror(errno));
699 return -errno;
700 }
701
702 // Fetch the payload
703 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
704 if (!cmsg)
705 return -EBADMSG;
706
707 *fd = *((int*)CMSG_DATA(cmsg));
708
709 CTX_DEBUG(jail->ctx, "Received fd %d from socket %d\n", *fd, socket);
710
711 return 0;
712}
713
714static int pakfire_jail_send_fd(struct pakfire_jail* jail, int socket, int fd) {
715 const size_t payload_length = sizeof(fd);
716 char buffer[CMSG_SPACE(payload_length)];
717 int r;
718
719 CTX_DEBUG(jail->ctx, "Sending fd %d to socket %d\n", fd, socket);
720
721 // Header
722 struct msghdr msg = {
723 .msg_control = buffer,
724 .msg_controllen = sizeof(buffer),
725 };
726
727 // Payload
728 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
729 cmsg->cmsg_level = SOL_SOCKET;
730 cmsg->cmsg_type = SCM_RIGHTS;
731 cmsg->cmsg_len = CMSG_LEN(payload_length);
732
733 // Set payload
734 *((int*)CMSG_DATA(cmsg)) = fd;
735
736 // Send the message
737 r = sendmsg(socket, &msg, 0);
738 if (r) {
739 CTX_ERROR(jail->ctx, "Could not send file descriptor: %s\n", strerror(errno));
740 return -errno;
741 }
742
743 return 0;
744}
745
195fe455
MT
746static int pakfire_jail_setup_pipe(struct pakfire_jail* jail, int (*fds)[2], const int flags) {
747 int r = pipe2(*fds, flags);
748 if (r < 0) {
749 ERROR(jail->pakfire, "Could not setup pipe: %m\n");
750 return 1;
751 }
752
753 return 0;
754}
755
756static void pakfire_jail_close_pipe(struct pakfire_jail* jail, int fds[2]) {
757 for (unsigned int i = 0; i < 2; i++)
dd2d7dbb 758 if (fds[i] >= 0)
195fe455
MT
759 close(fds[i]);
760}
761
e33387d3
MT
762/*
763 This is a convenience function to fetch the reading end of a pipe and
764 closes the write end.
765*/
06b864ae 766static int pakfire_jail_get_pipe_to_read(struct pakfire_jail* jail, int (*fds)[2]) {
e33387d3
MT
767 // Give the variables easier names to avoid confusion
768 int* fd_read = &(*fds)[0];
769 int* fd_write = &(*fds)[1];
770
771 // Close the write end of the pipe
4c3bab92 772 if (*fd_write >= 0) {
e33387d3 773 close(*fd_write);
d2eaf8dc 774 *fd_write = -1;
e33387d3
MT
775 }
776
777 // Return the read end
4c3bab92
MT
778 if (*fd_read >= 0)
779 return *fd_read;
780
781 return -1;
e33387d3
MT
782}
783
06b864ae
MT
784static int pakfire_jail_get_pipe_to_write(struct pakfire_jail* jail, int (*fds)[2]) {
785 // Give the variables easier names to avoid confusion
786 int* fd_read = &(*fds)[0];
787 int* fd_write = &(*fds)[1];
788
789 // Close the read end of the pipe
4c3bab92 790 if (*fd_read >= 0) {
06b864ae 791 close(*fd_read);
d2eaf8dc 792 *fd_read = -1;
06b864ae
MT
793 }
794
795 // Return the write end
4c3bab92
MT
796 if (*fd_write >= 0)
797 return *fd_write;
798
799 return -1;
06b864ae
MT
800}
801
19e50d86
MT
802static int pakfire_jail_log(struct pakfire* pakfire, void* data, int priority,
803 const char* line, const size_t length) {
804 // Pass everything to the parent logger
805 pakfire_log_condition(pakfire, priority, 0, "%.*s", (int)length, line);
806
807 return 0;
808}
809
e4fded3a
MT
810static int pakfire_jail_epoll_add_fd(struct pakfire_jail* jail, int epollfd, int fd, int events) {
811 struct epoll_event event = {
812 .events = events|EPOLLHUP,
813 .data = {
814 .fd = fd,
815 },
816 };
817 int r;
818
819 // Read flags
820 int flags = fcntl(fd, F_GETFL, 0);
821
822 // Set modified flags
823 r = fcntl(fd, F_SETFL, flags|O_NONBLOCK);
824 if (r < 0) {
825 CTX_ERROR(jail->ctx, "Could not set file descriptor %d into non-blocking mode: %s\n",
826 fd, strerror(errno));
827 return -errno;
828 }
829
830 // Add the file descriptor to the loop
831 r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
832 if (r < 0) {
833 ERROR(jail->pakfire, "Could not add file descriptor %d to epoll(): %s\n",
834 fd, strerror(errno));
835 return -errno;
836 }
837
838 return 0;
839}
840
d853213d 841static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
616f1fca 842 int epollfd = -1;
616f1fca 843 struct epoll_event events[EPOLL_MAX_EVENTS];
335b8a44 844 char garbage[8];
616f1fca
MT
845 int r = 0;
846
847 // Fetch file descriptors from context
06b864ae
MT
848 const int stdin = pakfire_jail_get_pipe_to_write(jail, &ctx->pipes.stdin);
849 const int stdout = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stdout);
850 const int stderr = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stderr);
f949feac 851 const int pidfd = ctx->pidfd;
616f1fca 852
8f49d254
MT
853 // Fetch the UNIX domain socket
854 const int socket_recv = pakfire_jail_get_pipe_to_read(jail, &ctx->socket);
855
335b8a44
MT
856 // Timer
857 const int timerfd = pakfire_jail_create_timer(jail);
858
e33387d3 859 // Logging
06b864ae
MT
860 const int log_INFO = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_INFO);
861 const int log_ERROR = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_ERROR);
6ac51607 862#ifdef ENABLE_DEBUG
06b864ae 863 const int log_DEBUG = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_DEBUG);
6ac51607 864#endif /* ENABLE_DEBUG */
7ebfb7cb
MT
865
866 // Make a list of all file descriptors we are interested in
e4fded3a
MT
867 const struct pakfire_wait_fds {
868 const int fd;
869 const int events;
870 } fds[] = {
871 // Standard input/output
872 { stdin, EPOLLOUT },
873 { stdout, EPOLLIN },
874 { stderr, EPOLLIN },
875
876 // Timer
877 { timerfd, EPOLLIN },
878
879 // Child Process
880 { ctx->pidfd, EPOLLIN },
881
882 // Log Pipes
883 { log_INFO, EPOLLIN },
884 { log_ERROR, EPOLLIN },
6ac51607 885#ifdef ENABLE_DEBUG
e4fded3a 886 { log_DEBUG, EPOLLIN },
6ac51607 887#endif /* ENABLE_DEBUG */
e4fded3a 888
8f49d254
MT
889 // UNIX Domain Socket
890 { socket_recv, EPOLLIN },
891
e4fded3a
MT
892 // Sentinel
893 { -1, 0 },
616f1fca
MT
894 };
895
896 // Setup epoll
897 epollfd = epoll_create1(0);
898 if (epollfd < 0) {
899 ERROR(jail->pakfire, "Could not initialize epoll(): %m\n");
900 r = 1;
d853213d 901 goto ERROR;
616f1fca
MT
902 }
903
616f1fca 904 // Turn file descriptors into non-blocking mode and add them to epoll()
e4fded3a 905 for (const struct pakfire_wait_fds* fd = fds; fd->events; fd++) {
d853213d 906 // Skip fds which were not initialized
e4fded3a 907 if (fd->fd < 0)
d853213d
MT
908 continue;
909
e4fded3a
MT
910 // Add the FD to the event loop
911 r = pakfire_jail_epoll_add_fd(jail, epollfd, fd->fd, fd->events);
912 if (r)
f949feac 913 goto ERROR;
616f1fca
MT
914 }
915
916 int ended = 0;
917
918 // Loop for as long as the process is alive
919 while (!ended) {
616f1fca
MT
920 int num = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, -1);
921 if (num < 1) {
922 // Ignore if epoll_wait() has been interrupted
923 if (errno == EINTR)
924 continue;
925
926 ERROR(jail->pakfire, "epoll_wait() failed: %m\n");
927 r = 1;
928
d853213d 929 goto ERROR;
616f1fca
MT
930 }
931
616f1fca 932 for (int i = 0; i < num; i++) {
e068b964 933 int e = events[i].events;
616f1fca
MT
934 int fd = events[i].data.fd;
935
e33387d3 936 struct pakfire_log_buffer* buffer = NULL;
2015cb92 937 pakfire_jail_communicate_out callback = NULL;
e33387d3
MT
938 void* data = NULL;
939 int priority;
940
e068b964
MT
941 // Check if there is any data to be read
942 if (e & EPOLLIN) {
f949feac
MT
943 // Handle any changes to the PIDFD
944 if (fd == pidfd) {
945 // Call waidid() and store the result
946 r = waitid(P_PIDFD, ctx->pidfd, &ctx->status, WEXITED);
e068b964 947 if (r) {
f949feac 948 ERROR(jail->pakfire, "waitid() failed: %m\n");
e068b964
MT
949 goto ERROR;
950 }
d853213d 951
e068b964
MT
952 // Mark that we have ended so that we will process the remaining
953 // events from epoll() now, but won't restart the outer loop.
954 ended = 1;
955 continue;
d853213d 956
335b8a44
MT
957 // Handle timer events
958 } else if (fd == timerfd) {
959 DEBUG(jail->pakfire, "Timer event received\n");
960
961 // Disarm the timer
962 r = read(timerfd, garbage, sizeof(garbage));
963 if (r < 1) {
964 ERROR(jail->pakfire, "Could not disarm timer: %m\n");
965 r = 1;
966 goto ERROR;
967 }
968
969 // Terminate the process if it hasn't already ended
970 if (!ended) {
971 DEBUG(jail->pakfire, "Terminating process...\n");
972
973 // Send SIGTERM to the process
f949feac 974 r = pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
335b8a44
MT
975 if (r) {
976 ERROR(jail->pakfire, "Could not kill process: %m\n");
977 goto ERROR;
978 }
979 }
980
9b171c6a
MT
981 // Don't fall through to log processing
982 continue;
47d2da8e 983
e068b964
MT
984 // Handle logging messages
985 } else if (fd == log_INFO) {
986 buffer = &ctx->buffers.log_INFO;
987 priority = LOG_INFO;
e33387d3 988
19e50d86 989 callback = pakfire_jail_log;
e33387d3 990
e068b964
MT
991 } else if (fd == log_ERROR) {
992 buffer = &ctx->buffers.log_ERROR;
993 priority = LOG_ERR;
e33387d3 994
19e50d86 995 callback = pakfire_jail_log;
e33387d3 996
6ac51607 997#ifdef ENABLE_DEBUG
e068b964
MT
998 } else if (fd == log_DEBUG) {
999 buffer = &ctx->buffers.log_DEBUG;
1000 priority = LOG_DEBUG;
e33387d3 1001
19e50d86 1002 callback = pakfire_jail_log;
6ac51607 1003#endif /* ENABLE_DEBUG */
e33387d3 1004
e068b964
MT
1005 // Handle anything from the log pipes
1006 } else if (fd == stdout) {
1007 buffer = &ctx->buffers.stdout;
1008 priority = LOG_INFO;
616f1fca 1009
2dc104da
MT
1010 // Send any output to the default logger if no callback is set
1011 if (ctx->communicate.out) {
1012 callback = ctx->communicate.out;
1013 data = ctx->communicate.data;
1014 } else {
1015 callback = jail->callbacks.log;
1016 data = jail->callbacks.log_data;
1017 }
e33387d3 1018
e068b964
MT
1019 } else if (fd == stderr) {
1020 buffer = &ctx->buffers.stderr;
1021 priority = LOG_ERR;
616f1fca 1022
2dc104da
MT
1023 // Send any output to the default logger if no callback is set
1024 if (ctx->communicate.out) {
1025 callback = ctx->communicate.out;
1026 data = ctx->communicate.data;
1027 } else {
1028 callback = jail->callbacks.log;
1029 data = jail->callbacks.log_data;
1030 }
e33387d3 1031
8f49d254
MT
1032 // Handle socket messages
1033 } else if (fd == socket_recv) {
1034 // Receive the passed FD
1035 r = pakfire_jail_recv_fd(jail, socket_recv, &fd);
1036 if (r)
1037 goto ERROR;
1038
1039 // XXX Do something with the file descriptor
1040
1041 // Don't fall through to log processing
1042 continue;
1043
e068b964
MT
1044 } else {
1045 DEBUG(jail->pakfire, "Received invalid file descriptor %d\n", fd);
1046 continue;
1047 }
1048
1049 // Handle log event
1050 r = pakfire_jail_handle_log(jail, ctx, priority, fd, buffer, callback, data);
1051 if (r)
1052 goto ERROR;
616f1fca
MT
1053 }
1054
06b864ae
MT
1055 if (e & EPOLLOUT) {
1056 // Handle standard input
1057 if (fd == stdin) {
1058 r = pakfire_jail_stream_stdin(jail, ctx, fd);
1059 if (r) {
1060 switch (errno) {
1061 // Ignore if we filled up the buffer
1062 case EAGAIN:
1063 break;
1064
1065 default:
1066 ERROR(jail->pakfire, "Could not write to stdin: %m\n");
1067 goto ERROR;
1068 }
1069 }
1070 }
1071 }
1072
e068b964
MT
1073 // Check if any file descriptors have been closed
1074 if (e & EPOLLHUP) {
1075 // Remove the file descriptor
1076 r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
1077 if (r) {
1078 ERROR(jail->pakfire, "Could not remove closed file-descriptor %d: %m\n", fd);
1079 goto ERROR;
1080 }
1081 }
616f1fca
MT
1082 }
1083 }
1084
d853213d 1085ERROR:
dd2d7dbb 1086 if (epollfd >= 0)
616f1fca 1087 close(epollfd);
dd2d7dbb 1088 if (timerfd >= 0)
335b8a44 1089 close(timerfd);
616f1fca
MT
1090
1091 return r;
1092}
1093
ccdd2e95
MT
1094int pakfire_jail_capture_stdout(struct pakfire* pakfire, void* data,
1095 int priority, const char* line, size_t length) {
12b9b39f
MT
1096 char** output = (char**)data;
1097 int r;
0de6bb30 1098
2015cb92
MT
1099 // Append everything from stdout to a buffer
1100 if (output && priority == LOG_INFO) {
12b9b39f
MT
1101 r = asprintf(output, "%s%s", (output && *output) ? *output : "", line);
1102 if (r < 0)
0de6bb30 1103 return 1;
0de6bb30
MT
1104 return 0;
1105 }
1106
1107 // Send everything else to the default logger
1108 return pakfire_jail_default_log_callback(pakfire, NULL, priority, line, length);
1109}
1110
980b15af
MT
1111// Capabilities
1112
e6791c52
MT
1113// Logs all capabilities of the current process
1114static int pakfire_jail_show_capabilities(struct pakfire_jail* jail) {
1115 cap_t caps = NULL;
1116 char* name = NULL;
1117 cap_flag_value_t value_e;
1118 cap_flag_value_t value_i;
1119 cap_flag_value_t value_p;
1120 int r;
980b15af 1121
e6791c52
MT
1122 // Fetch PID
1123 pid_t pid = getpid();
980b15af 1124
e6791c52
MT
1125 // Fetch all capabilities
1126 caps = cap_get_proc();
1127 if (!caps) {
1128 ERROR(jail->pakfire, "Could not fetch capabilities: %m\n");
1129 r = 1;
1130 goto ERROR;
1131 }
980b15af 1132
e6791c52 1133 DEBUG(jail->pakfire, "Capabilities of PID %d:\n", pid);
980b15af 1134
e6791c52
MT
1135 // Iterate over all capabilities
1136 for (unsigned int cap = 0; cap_valid(cap); cap++) {
1137 name = cap_to_name(cap);
980b15af 1138
e6791c52
MT
1139 // Fetch effective value
1140 r = cap_get_flag(caps, cap, CAP_EFFECTIVE, &value_e);
1141 if (r)
1142 goto ERROR;
980b15af 1143
e6791c52
MT
1144 // Fetch inheritable value
1145 r = cap_get_flag(caps, cap, CAP_INHERITABLE, &value_i);
1146 if (r)
1147 goto ERROR;
980b15af 1148
e6791c52
MT
1149 // Fetch permitted value
1150 r = cap_get_flag(caps, cap, CAP_PERMITTED, &value_p);
1151 if (r)
1152 goto ERROR;
980b15af 1153
e6791c52
MT
1154 DEBUG(jail->pakfire,
1155 " %-24s : %c%c%c\n",
1156 name,
1157 (value_e == CAP_SET) ? 'e' : '-',
1158 (value_i == CAP_SET) ? 'i' : '-',
1159 (value_p == CAP_SET) ? 'p' : '-'
1160 );
980b15af 1161
e6791c52
MT
1162 // Free name
1163 cap_free(name);
1164 name = NULL;
1165 }
980b15af 1166
e6791c52
MT
1167 // Success
1168 r = 0;
980b15af 1169
e6791c52
MT
1170ERROR:
1171 if (name)
1172 cap_free(name);
1173 if (caps)
1174 cap_free(caps);
980b15af 1175
e6791c52
MT
1176 return r;
1177}
980b15af 1178
e6791c52
MT
1179static int pakfire_jail_set_capabilities(struct pakfire_jail* jail) {
1180 cap_t caps = NULL;
1181 char* name = NULL;
1182 int r;
980b15af 1183
e6791c52
MT
1184 // Fetch capabilities
1185 caps = cap_get_proc();
1186 if (!caps) {
1187 ERROR(jail->pakfire, "Could not read capabilities: %m\n");
1188 r = 1;
1189 goto ERROR;
1190 }
980b15af 1191
e6791c52
MT
1192 // Walk through all capabilities
1193 for (cap_value_t cap = 0; cap_valid(cap); cap++) {
1194 cap_value_t _caps[] = { cap };
980b15af 1195
e6791c52
MT
1196 // Fetch the name of the capability
1197 name = cap_to_name(cap);
980b15af 1198
e6791c52
MT
1199 r = cap_set_flag(caps, CAP_EFFECTIVE, 1, _caps, CAP_SET);
1200 if (r) {
1201 ERROR(jail->pakfire, "Could not set %s: %m\n", name);
1202 goto ERROR;
1203 }
980b15af 1204
e6791c52 1205 r = cap_set_flag(caps, CAP_INHERITABLE, 1, _caps, CAP_SET);
980b15af 1206 if (r) {
e6791c52
MT
1207 ERROR(jail->pakfire, "Could not set %s: %m\n", name);
1208 goto ERROR;
980b15af
MT
1209 }
1210
e6791c52
MT
1211 r = cap_set_flag(caps, CAP_PERMITTED, 1, _caps, CAP_SET);
1212 if (r) {
1213 ERROR(jail->pakfire, "Could not set %s: %m\n", name);
1214 goto ERROR;
1215 }
980b15af 1216
e6791c52
MT
1217 // Free name
1218 cap_free(name);
1219 name = NULL;
980b15af
MT
1220 }
1221
e6791c52
MT
1222 // Restore all capabilities
1223 r = cap_set_proc(caps);
980b15af 1224 if (r) {
e6791c52 1225 ERROR(jail->pakfire, "Restoring capabilities failed: %m\n");
980b15af
MT
1226 goto ERROR;
1227 }
1228
e6791c52
MT
1229 // Add all capabilities to the ambient set
1230 for (unsigned int cap = 0; cap_valid(cap); cap++) {
1231 name = cap_to_name(cap);
1232
1233 // Raise the capability
1234 r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0);
1235 if (r) {
1236 ERROR(jail->pakfire, "Could not set ambient capability %s: %m\n", name);
1237 goto ERROR;
1238 }
1239
1240 // Free name
1241 cap_free(name);
1242 name = NULL;
980b15af
MT
1243 }
1244
e6791c52
MT
1245 // Success
1246 r = 0;
1247
980b15af 1248ERROR:
e6791c52
MT
1249 if (name)
1250 cap_free(name);
980b15af
MT
1251 if (caps)
1252 cap_free(caps);
1253
1254 return r;
1255}
1256
739d5b57
MT
1257// Syscall Filter
1258
1259static int pakfire_jail_limit_syscalls(struct pakfire_jail* jail) {
1260 const int syscalls[] = {
1261 // The kernel's keyring isn't namespaced
1262 SCMP_SYS(keyctl),
1263 SCMP_SYS(add_key),
1264 SCMP_SYS(request_key),
1265
1266 // Disable userfaultfd
1267 SCMP_SYS(userfaultfd),
1268
1269 // Disable perf which could leak a lot of information about the host
1270 SCMP_SYS(perf_event_open),
1271
1272 0,
1273 };
1274 int r = 1;
1275
1276 DEBUG(jail->pakfire, "Applying syscall filter...\n");
1277
1278 // Setup a syscall filter which allows everything by default
1279 scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
1280 if (!ctx) {
1281 ERROR(jail->pakfire, "Could not setup seccomp filter: %m\n");
1282 goto ERROR;
1283 }
1284
1285 // All all syscalls
1286 for (const int* syscall = syscalls; *syscall; syscall++) {
1287 r = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), *syscall, 0);
1288 if (r) {
1289 ERROR(jail->pakfire, "Could not configure syscall %d: %m\n", *syscall);
1290 goto ERROR;
1291 }
1292 }
1293
1294 // Load syscall filter into the kernel
1295 r = seccomp_load(ctx);
1296 if (r) {
1297 ERROR(jail->pakfire, "Could not load syscall filter into the kernel: %m\n");
1298 goto ERROR;
1299 }
1300
1301ERROR:
1302 if (ctx)
1303 seccomp_release(ctx);
1304
1305 return r;
1306}
1307
cc6e2264
MT
1308// Mountpoints
1309
061223f7 1310PAKFIRE_EXPORT int pakfire_jail_bind(struct pakfire_jail* jail,
cc6e2264
MT
1311 const char* source, const char* target, int flags) {
1312 struct pakfire_jail_mountpoint* mp = NULL;
1313 int r;
1314
1315 // Check if there is any space left
1316 if (jail->num_mountpoints >= MAX_MOUNTPOINTS) {
1317 errno = ENOSPC;
1318 return 1;
1319 }
1320
1321 // Check for valid inputs
1322 if (!source || !target) {
1323 errno = EINVAL;
1324 return 1;
1325 }
1326
1327 // Select the next free slot
1328 mp = &jail->mountpoints[jail->num_mountpoints];
1329
1330 // Copy source
1331 r = pakfire_string_set(mp->source, source);
a60955af 1332 if (r) {
cc6e2264 1333 ERROR(jail->pakfire, "Could not copy source: %m\n");
a60955af 1334 return r;
cc6e2264
MT
1335 }
1336
1337 // Copy target
1338 r = pakfire_string_set(mp->target, target);
a60955af 1339 if (r) {
cc6e2264 1340 ERROR(jail->pakfire, "Could not copy target: %m\n");
a60955af 1341 return r;
cc6e2264
MT
1342 }
1343
1344 // Copy flags
1345 mp->flags = flags;
1346
1347 // Increment counter
1348 jail->num_mountpoints++;
1349
1350 return 0;
1351}
1352
7bdf1d8e
MT
1353static int pakfire_jail_mount_networking(struct pakfire_jail* jail) {
1354 int r;
1355
1356 const char* paths[] = {
1357 "/etc/hosts",
1358 "/etc/resolv.conf",
1359 NULL,
1360 };
1361
1362 // Bind-mount all paths read-only
1363 for (const char** path = paths; *path; path++) {
1364 r = pakfire_bind(jail->pakfire, *path, NULL, MS_RDONLY);
693a979a
MT
1365 if (r) {
1366 switch (errno) {
1367 // Ignore if we don't have permission
1368 case EPERM:
1369 continue;
1370
1371 default:
1372 break;
1373 }
7bdf1d8e 1374 return r;
693a979a 1375 }
7bdf1d8e
MT
1376 }
1377
1378 return 0;
1379}
1380
cc6e2264
MT
1381/*
1382 Mounts everything that we require in the new namespace
1383*/
7bdf1d8e 1384static int pakfire_jail_mount(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
cc6e2264 1385 struct pakfire_jail_mountpoint* mp = NULL;
282b732a 1386 int flags = 0;
cc6e2264
MT
1387 int r;
1388
282b732a
MT
1389 // Enable loop devices
1390 if (pakfire_jail_exec_has_flag(ctx, PAKFIRE_JAIL_HAS_LOOP_DEVICES))
1391 flags |= PAKFIRE_MOUNT_LOOP_DEVICES;
1392
cc6e2264 1393 // Mount all default stuff
b2ccb363
MT
1394 r = pakfire_mount_all(jail->pakfire, PAKFIRE_MNTNS_OUTER, flags);
1395 if (r)
1396 return r;
1397
1398 // Populate /dev
1399 r = pakfire_populate_dev(jail->pakfire, flags);
1400 if (r)
1401 return r;
1402
1403 // Mount the interpreter (if needed)
1404 r = pakfire_mount_interpreter(jail->pakfire);
cc6e2264
MT
1405 if (r)
1406 return r;
1407
7bdf1d8e
MT
1408 // Mount networking stuff
1409 if (pakfire_jail_exec_has_flag(ctx, PAKFIRE_JAIL_HAS_NETWORKING)) {
1410 r = pakfire_jail_mount_networking(jail);
1411 if (r)
1412 return r;
1413 }
1414
cc6e2264
MT
1415 // Mount all custom stuff
1416 for (unsigned int i = 0; i < jail->num_mountpoints; i++) {
1417 // Fetch mountpoint
1418 mp = &jail->mountpoints[i];
1419
1420 // Mount it
1421 r = pakfire_bind(jail->pakfire, mp->source, mp->target, mp->flags);
1422 if (r)
1423 return r;
1424 }
1425
cc6e2264
MT
1426 return 0;
1427}
1428
fec79a33
MT
1429// Networking
1430
1431static int pakfire_jail_setup_loopback(struct pakfire_jail* jail) {
1432 struct nl_sock* nl = NULL;
1433 struct nl_cache* cache = NULL;
1434 struct rtnl_link* link = NULL;
1435 struct rtnl_link* change = NULL;
1436 int r;
1437
1438 DEBUG(jail->pakfire, "Setting up loopback...\n");
1439
1440 // Allocate a netlink socket
1441 nl = nl_socket_alloc();
1442 if (!nl) {
1443 ERROR(jail->pakfire, "Could not allocate a netlink socket: %m\n");
1444 r = 1;
1445 goto ERROR;
1446 }
1447
1448 // Connect the socket
1449 r = nl_connect(nl, NETLINK_ROUTE);
1450 if (r) {
1451 ERROR(jail->pakfire, "Could not connect netlink socket: %s\n", nl_geterror(r));
1452 goto ERROR;
1453 }
1454
1455 // Allocate the netlink cache
1456 r = rtnl_link_alloc_cache(nl, AF_UNSPEC, &cache);
1457 if (r < 0) {
1458 ERROR(jail->pakfire, "Unable to allocate netlink cache: %s\n", nl_geterror(r));
1459 goto ERROR;
1460 }
1461
1462 // Fetch loopback interface
1463 link = rtnl_link_get_by_name(cache, "lo");
1464 if (!link) {
1465 ERROR(jail->pakfire, "Could not find lo interface. Ignoring.\n");
1466 r = 0;
1467 goto ERROR;
1468 }
1469
1470 // Allocate a new link
1471 change = rtnl_link_alloc();
1472 if (!change) {
1473 ERROR(jail->pakfire, "Could not allocate change link\n");
1474 r = 1;
1475 goto ERROR;
1476 }
1477
1478 // Set the link to UP
1479 rtnl_link_set_flags(change, IFF_UP);
1480
1481 // Apply any changes
1482 r = rtnl_link_change(nl, link, change, 0);
1483 if (r) {
1484 ERROR(jail->pakfire, "Unable to activate loopback: %s\n", nl_geterror(r));
1485 goto ERROR;
1486 }
1487
1488 // Success
1489 r = 0;
1490
1491ERROR:
1492 if (nl)
1493 nl_socket_free(nl);
1494
1495 return r;
1496}
1497
679ee2fa
MT
1498// UID/GID Mapping
1499
679ee2fa
MT
1500static int pakfire_jail_setup_uid_mapping(struct pakfire_jail* jail, pid_t pid) {
1501 char path[PATH_MAX];
1502 int r;
1503
4896e62c
MT
1504 // Skip mapping anything when running on /
1505 if (pakfire_on_root(jail->pakfire))
1506 return 0;
0f7f068b 1507
abe4ee37
MT
1508 // Make path
1509 r = pakfire_string_format(path, "/proc/%d/uid_map", pid);
1510 if (r)
1511 return r;
1512
1513 // Fetch UID
1514 const uid_t uid = pakfire_uid(jail->pakfire);
1515
4896e62c 1516 // Fetch SUBUID
a1ff2863 1517 const struct pakfire_subid* subuid = pakfire_subuid(jail->pakfire);
4896e62c
MT
1518 if (!subuid)
1519 return 1;
679ee2fa 1520
abe4ee37 1521 /* When running as root, we will map the entire range.
679ee2fa 1522
abe4ee37
MT
1523 When running as a non-privileged user, we will map the root user inside the jail
1524 to the user's UID outside of the jail, and we will map the rest starting from one.
1525 */
679ee2fa 1526
abe4ee37
MT
1527 // Running as root
1528 if (uid == 0) {
1529 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
1530 "0 %lu %lu\n", subuid->id, subuid->length);
1531 } else {
1532 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
b64888fa 1533 "0 %lu 1\n1 %lu %lu\n", uid, subuid->id, subuid->length);
abe4ee37
MT
1534 }
1535
1536 if (r) {
1537 ERROR(jail->pakfire, "Could not map UIDs: %m\n");
1538 return r;
1539 }
1540
1541 return r;
679ee2fa
MT
1542}
1543
1544static int pakfire_jail_setup_gid_mapping(struct pakfire_jail* jail, pid_t pid) {
1545 char path[PATH_MAX];
1546 int r;
1547
4896e62c
MT
1548 // Skip mapping anything when running on /
1549 if (pakfire_on_root(jail->pakfire))
1550 return 0;
0f7f068b 1551
abe4ee37
MT
1552 // Fetch GID
1553 const gid_t gid = pakfire_gid(jail->pakfire);
1554
4896e62c 1555 // Fetch SUBGID
a1ff2863 1556 const struct pakfire_subid* subgid = pakfire_subgid(jail->pakfire);
4896e62c
MT
1557 if (!subgid)
1558 return 1;
679ee2fa
MT
1559
1560 // Make path
1561 r = pakfire_string_format(path, "/proc/%d/gid_map", pid);
a60955af
MT
1562 if (r)
1563 return r;
679ee2fa 1564
abe4ee37
MT
1565 // Running as root
1566 if (gid == 0) {
1567 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
1568 "0 %lu %lu\n", subgid->id, subgid->length);
1569 } else {
1570 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
b9a1d857 1571 "0 %lu 1\n1 %lu %lu\n", gid, subgid->id, subgid->length);
abe4ee37 1572 }
679ee2fa 1573
abe4ee37
MT
1574 if (r) {
1575 ERROR(jail->pakfire, "Could not map GIDs: %m\n");
1576 return r;
1577 }
1578
1579 return r;
679ee2fa
MT
1580}
1581
78d7488a
MT
1582static int pakfire_jail_setgroups(struct pakfire_jail* jail, pid_t pid) {
1583 char path[PATH_MAX];
fe30bb0a 1584 int r;
78d7488a
MT
1585
1586 // Make path
1587 r = pakfire_string_format(path, "/proc/%d/setgroups", pid);
a60955af
MT
1588 if (r)
1589 return r;
78d7488a 1590
fe30bb0a 1591 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0, "deny\n");
78d7488a 1592 if (r) {
fe30bb0a
MT
1593 CTX_ERROR(jail->ctx, "Could not set setgroups to deny: %s\n", strerror(errno));
1594 r = -errno;
78d7488a
MT
1595 }
1596
78d7488a
MT
1597 return r;
1598}
1599
43dc0e16 1600static int pakfire_jail_send_signal(struct pakfire_jail* jail, int fd) {
743f449e
MT
1601 const uint64_t val = 1;
1602 int r = 0;
43dc0e16
MT
1603
1604 DEBUG(jail->pakfire, "Sending signal...\n");
1605
743f449e 1606 // Write to the file descriptor
a87c52e2
MT
1607 r = eventfd_write(fd, val);
1608 if (r < 0) {
1609 ERROR(jail->pakfire, "Could not send signal: %s\n", strerror(errno));
1610 r = -errno;
743f449e
MT
1611 }
1612
1613 // Close the file descriptor
43dc0e16
MT
1614 close(fd);
1615
743f449e 1616 return r;
43dc0e16
MT
1617}
1618
1619static int pakfire_jail_wait_for_signal(struct pakfire_jail* jail, int fd) {
743f449e
MT
1620 uint64_t val = 0;
1621 int r = 0;
43dc0e16
MT
1622
1623 DEBUG(jail->pakfire, "Waiting for signal...\n");
1624
a87c52e2
MT
1625 r = eventfd_read(fd, &val);
1626 if (r < 0) {
1627 ERROR(jail->pakfire, "Error waiting for signal: %s\n", strerror(errno));
1628 r = -errno;
743f449e
MT
1629 }
1630
1631 // Close the file descriptor
43dc0e16
MT
1632 close(fd);
1633
743f449e 1634 return r;
43dc0e16
MT
1635}
1636
f949feac
MT
1637/*
1638 Performs the initialisation that needs to happen in the parent part
1639*/
1640static int pakfire_jail_parent(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
1641 int r;
1642
1643 // Setup UID mapping
1644 r = pakfire_jail_setup_uid_mapping(jail, ctx->pid);
1645 if (r)
1646 return r;
1647
1648 // Write "deny" to /proc/PID/setgroups
1649 r = pakfire_jail_setgroups(jail, ctx->pid);
1650 if (r)
1651 return r;
1652
1653 // Setup GID mapping
1654 r = pakfire_jail_setup_gid_mapping(jail, ctx->pid);
1655 if (r)
1656 return r;
1657
1658 // Parent has finished initialisation
1659 DEBUG(jail->pakfire, "Parent has finished initialization\n");
1660
1661 // Send signal to client
1662 r = pakfire_jail_send_signal(jail, ctx->completed_fd);
1663 if (r)
1664 return r;
1665
1666 return 0;
1667}
1668
14df7388
MT
1669static int pakfire_jail_switch_root(struct pakfire_jail* jail, const char* root) {
1670 int r;
1671
1672 // Change to the new root
1673 r = chdir(root);
1674 if (r) {
1675 ERROR(jail->pakfire, "chdir(%s) failed: %m\n", root);
1676 return r;
1677 }
1678
1679 // Switch Root!
1680 r = pivot_root(".", ".");
1681 if (r) {
1682 ERROR(jail->pakfire, "Failed changing into the new root directory %s: %m\n", root);
1683 return r;
1684 }
1685
1686 // Umount the old root
1687 r = umount2(".", MNT_DETACH);
1688 if (r) {
1689 ERROR(jail->pakfire, "Could not umount the old root filesystem: %m\n");
1690 return r;
1691 }
1692
1693 return 0;
1694}
1695
374e6268
MT
1696#if 0
1697static int pakfire_jail_open_pty(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
1698 int r;
1699
1700 // Allocate a new PTY
1701 ctx->consolefd = posix_openpt(O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
1702 if (ctx->consolefd < 0)
1703 return -errno;
1704
1705 // Fetch the path
1706 r = ptsname_r(ctx->consolefd, ctx->console, sizeof(ctx->console));
1707 if (r)
1708 return -r;
1709
1710 CTX_DEBUG(jail->ctx, "Allocated console at %s (%d)\n", ctx->console, ctx->consolefd);
1711
1712 // Create a symlink
1713 r = pakfire_symlink(jail->ctx, "/dev/console", ctx->console);
1714 if (r)
1715 return r;
1716
1717 return r;
1718}
1719#endif
1720
f949feac
MT
1721static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
1722 const char* argv[]) {
43dc0e16
MT
1723 int r;
1724
f949feac
MT
1725 // Redirect any logging to our log pipe
1726 pakfire_ctx_set_log_callback(jail->ctx, pakfire_jail_log_redirect, &ctx->pipes);
0bd84dc1 1727
f949feac
MT
1728 // Fetch my own PID
1729 pid_t pid = getpid();
9b171c6a 1730
f949feac 1731 DEBUG(jail->pakfire, "Launched child process in jail with PID %d\n", pid);
9b171c6a 1732
f949feac
MT
1733 // Wait for the parent to finish initialization
1734 r = pakfire_jail_wait_for_signal(jail, ctx->completed_fd);
43dc0e16
MT
1735 if (r)
1736 return r;
1737
f949feac
MT
1738 // Die with parent
1739 r = prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
1740 if (r) {
1741 ERROR(jail->pakfire, "Could not configure to die with parent: %m\n");
1742 return 126;
1743 }
9b171c6a 1744
90503c53
MT
1745 // Make this process dumpable
1746 r = prctl (PR_SET_DUMPABLE, 1, 0, 0, 0);
1747 if (r) {
f949feac 1748 ERROR(jail->pakfire, "Could not make the process dumpable: %m\n");
90503c53
MT
1749 return 126;
1750 }
1751
1752 // Don't drop any capabilities on setuid()
1753 r = prctl(PR_SET_KEEPCAPS, 1);
1754 if (r) {
f949feac 1755 ERROR(jail->pakfire, "Could not set PR_SET_KEEPCAPS: %m\n");
90503c53
MT
1756 return 126;
1757 }
4f59c39b
MT
1758
1759 // Fetch UID/GID
1760 uid_t uid = getuid();
1761 gid_t gid = getgid();
1762
1763 // Fetch EUID/EGID
1764 uid_t euid = geteuid();
1765 gid_t egid = getegid();
1766
a8a41064
MT
1767 DEBUG(jail->pakfire, " UID: %u (effective %u)\n", uid, euid);
1768 DEBUG(jail->pakfire, " GID: %u (effective %u)\n", gid, egid);
4f59c39b 1769
7f572522
MT
1770 // Log all mountpoints
1771 pakfire_mount_list(jail->ctx);
1772
9b171c6a
MT
1773 // Fail if we are not PID 1
1774 if (pid != 1) {
1775 CTX_ERROR(jail->ctx, "Child process is not PID 1\n");
3a9f6931 1776 return 126;
9b171c6a
MT
1777 }
1778
1779 // Fail if we are not running as root
4f719e21 1780 if (uid || gid || euid || egid) {
4f59c39b 1781 ERROR(jail->pakfire, "Child process is not running as root\n");
3a9f6931 1782 return 126;
4f59c39b
MT
1783 }
1784
b2ccb363
MT
1785 // Mount all default stuff
1786 r = pakfire_mount_all(jail->pakfire, PAKFIRE_MNTNS_INNER, 0);
1787 if (r)
1788 return 126;
1789
374e6268
MT
1790#if 0
1791 // Create a new session
1792 r = setsid();
1793 if (r < 0) {
1794 CTX_ERROR(jail->ctx, "Could not create a new session: %s\n", strerror(errno));
1795 return 126;
1796 }
1797
1798 // Allocate a new PTY
1799 r = pakfire_jail_open_pty(jail, ctx);
1800 if (r) {
1801 CTX_ERROR(jail->ctx, "Could not allocate a new PTY: %s\n", strerror(-r));
1802 return 126;
1803 }
1804#endif
1805
f949feac 1806 const char* root = pakfire_get_path(jail->pakfire);
652f2a99 1807 const char* arch = pakfire_get_effective_arch(jail->pakfire);
4f59c39b 1808
f949feac
MT
1809 // Change mount propagation to slave to receive anything from the parent namespace
1810 r = pakfire_mount_change_propagation(jail->ctx, "/", MS_SLAVE);
1811 if (r)
1812 return r;
1813
1814 // Make root a mountpoint in the new mount namespace
1815 r = pakfire_mount_make_mounpoint(jail->pakfire, root);
1816 if (r)
1817 return r;
1818
1819 // Change mount propagation to private
1820 r = pakfire_mount_change_propagation(jail->ctx, root, MS_PRIVATE);
1821 if (r)
1822 return r;
1823
1824 // Change root (unless root is /)
1825 if (!pakfire_on_root(jail->pakfire)) {
1826 // Mount everything
1827 r = pakfire_jail_mount(jail, ctx);
1828 if (r)
1829 return r;
1830
1831 // chroot()
1832 r = pakfire_jail_switch_root(jail, root);
1833 if (r)
1834 return r;
1835 }
1836
90d92b5c
MT
1837 // Set personality
1838 unsigned long persona = pakfire_arch_personality(arch);
1839 if (persona) {
1840 r = personality(persona);
1841 if (r < 0) {
1842 ERROR(jail->pakfire, "Could not set personality (%x)\n", (unsigned int)persona);
f949feac 1843 return 1;
90d92b5c
MT
1844 }
1845 }
1846
fec79a33
MT
1847 // Setup networking
1848 if (!pakfire_jail_exec_has_flag(ctx, PAKFIRE_JAIL_HAS_NETWORKING)) {
1849 r = pakfire_jail_setup_loopback(jail);
1850 if (r)
1851 return 1;
1852 }
1853
cf440db8
MT
1854 // Set nice level
1855 if (jail->nice) {
1856 DEBUG(jail->pakfire, "Setting nice level to %d\n", jail->nice);
1857
1858 r = setpriority(PRIO_PROCESS, pid, jail->nice);
1859 if (r) {
1860 ERROR(jail->pakfire, "Could not set nice level: %m\n");
1861 return 1;
1862 }
1863 }
1864
e33387d3
MT
1865 // Close other end of log pipes
1866 close(ctx->pipes.log_INFO[0]);
1867 close(ctx->pipes.log_ERROR[0]);
1868#ifdef ENABLE_DEBUG
1869 close(ctx->pipes.log_DEBUG[0]);
1870#endif /* ENABLE_DEBUG */
1871
2015cb92 1872 // Connect standard input
bca69274 1873 if (ctx->pipes.stdin[0] >= 0) {
2015cb92
MT
1874 r = dup2(ctx->pipes.stdin[0], STDIN_FILENO);
1875 if (r < 0) {
1876 ERROR(jail->pakfire, "Could not connect fd %d to stdin: %m\n",
1877 ctx->pipes.stdin[0]);
1878
1879 return 1;
1880 }
1881 }
1882
7ebfb7cb 1883 // Connect standard output and error
bca69274 1884 if (ctx->pipes.stdout[1] >= 0 && ctx->pipes.stderr[1] >= 0) {
7ebfb7cb
MT
1885 r = dup2(ctx->pipes.stdout[1], STDOUT_FILENO);
1886 if (r < 0) {
1887 ERROR(jail->pakfire, "Could not connect fd %d to stdout: %m\n",
1888 ctx->pipes.stdout[1]);
1889
1890 return 1;
1891 }
1892
1893 r = dup2(ctx->pipes.stderr[1], STDERR_FILENO);
1894 if (r < 0) {
1895 ERROR(jail->pakfire, "Could not connect fd %d to stderr: %m\n",
1896 ctx->pipes.stderr[1]);
1897
1898 return 1;
1899 }
1900
195fe455 1901 // Close the pipe (as we have moved the original file descriptors)
2015cb92 1902 pakfire_jail_close_pipe(jail, ctx->pipes.stdin);
195fe455
MT
1903 pakfire_jail_close_pipe(jail, ctx->pipes.stdout);
1904 pakfire_jail_close_pipe(jail, ctx->pipes.stderr);
7ebfb7cb
MT
1905 }
1906
007bc66c
MT
1907 // Reset open file limit (http://0pointer.net/blog/file-descriptor-limits.html)
1908 r = pakfire_rlimit_reset_nofile(jail->pakfire);
1909 if (r)
1910 return r;
1911
e6791c52
MT
1912 // Set capabilities
1913 r = pakfire_jail_set_capabilities(jail);
1914 if (r)
1915 return r;
1916
1917 // Show capabilities
1918 r = pakfire_jail_show_capabilities(jail);
980b15af
MT
1919 if (r)
1920 return r;
1921
739d5b57
MT
1922 // Filter syscalls
1923 r = pakfire_jail_limit_syscalls(jail);
1924 if (r)
1925 return r;
1926
f949feac
MT
1927 DEBUG(jail->pakfire, "Child process initialization done\n");
1928 DEBUG(jail->pakfire, "Launching command:\n");
2015cb92
MT
1929
1930 // Log argv
1931 for (unsigned int i = 0; argv[i]; i++)
f949feac 1932 DEBUG(jail->pakfire, " argv[%u] = %s\n", i, argv[i]);
2015cb92 1933
b3498aeb
MT
1934 // exec() command
1935 r = execvpe(argv[0], (char**)argv, jail->env);
9fa1afb6
MT
1936 if (r < 0) {
1937 // Translate errno into regular exit code
1938 switch (errno) {
1939 case ENOENT:
1940 // Ignore if the command doesn't exist
1941 if (ctx->flags & PAKFIRE_JAIL_NOENT_OK)
1942 r = 0;
1943 else
1944 r = 127;
b3498aeb 1945
9fa1afb6 1946 break;
b3498aeb 1947
9fa1afb6
MT
1948 default:
1949 r = 1;
1950 }
1951
f949feac 1952 ERROR(jail->pakfire, "Could not execve(%s): %m\n", argv[0]);
b3498aeb
MT
1953 }
1954
1955 // We should not get here
1956 return r;
0bd84dc1
MT
1957}
1958
9f50bf71 1959// Run a command in the jail
db4f234f 1960static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[],
2015cb92
MT
1961 const int interactive,
1962 pakfire_jail_communicate_in communicate_in,
1963 pakfire_jail_communicate_out communicate_out,
9fa1afb6 1964 void* data, int flags) {
f949feac 1965 int exit = -1;
0bd84dc1
MT
1966 int r;
1967
b3498aeb
MT
1968 // Check if argv is valid
1969 if (!argv || !argv[0]) {
1970 errno = EINVAL;
1971 return -1;
1972 }
1973
616f1fca
MT
1974 // Initialize context for this call
1975 struct pakfire_jail_exec ctx = {
9fa1afb6 1976 .flags = flags,
7bdf1d8e 1977
8f49d254
MT
1978 .socket = { -1, -1 },
1979
616f1fca 1980 .pipes = {
4c3bab92
MT
1981 .stdin = { -1, -1 },
1982 .stdout = { -1, -1 },
1983 .stderr = { -1, -1 },
1984 .log_INFO = { -1, -1 },
1985 .log_ERROR = { -1, -1 },
6ac51607 1986#ifdef ENABLE_DEBUG
4c3bab92 1987 .log_DEBUG = { -1, -1 },
6ac51607 1988#endif /* ENABLE_DEBUG */
2015cb92
MT
1989 },
1990
1991 .communicate = {
1992 .in = communicate_in,
1993 .out = communicate_out,
1994 .data = data,
616f1fca 1995 },
d2eaf8dc 1996
f949feac 1997 .pidfd = -1,
616f1fca
MT
1998 };
1999
0bd84dc1
MT
2000 DEBUG(jail->pakfire, "Executing jail...\n");
2001
7bdf1d8e
MT
2002 // Enable networking in interactive mode
2003 if (interactive)
2004 ctx.flags |= PAKFIRE_JAIL_HAS_NETWORKING;
2005
43dc0e16
MT
2006 /*
2007 Setup a file descriptor which can be used to notify the client that the parent
2008 has completed configuration.
2009 */
f7d240a7
MT
2010 ctx.completed_fd = eventfd(0, EFD_CLOEXEC);
2011 if (ctx.completed_fd < 0) {
43dc0e16
MT
2012 ERROR(jail->pakfire, "eventfd() failed: %m\n");
2013 return -1;
2014 }
2015
8f49d254
MT
2016 // Create a UNIX domain socket
2017 r = socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ctx.socket);
2018 if (r < 0) {
2019 CTX_ERROR(jail->ctx, "Could not create UNIX socket: %s\n", strerror(errno));
2020 r = -errno;
2021 goto ERROR;
2022 }
2023
616f1fca 2024 // Create pipes to communicate with child process if we are not running interactively
58963c75 2025 if (!interactive) {
2015cb92
MT
2026 // stdin (only if callback is set)
2027 if (ctx.communicate.in) {
2028 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdin, 0);
2029 if (r)
2030 goto ERROR;
2031 }
2032
616f1fca 2033 // stdout
e33387d3
MT
2034 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdout, 0);
2035 if (r)
616f1fca 2036 goto ERROR;
616f1fca
MT
2037
2038 // stderr
e33387d3
MT
2039 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stderr, 0);
2040 if (r)
616f1fca 2041 goto ERROR;
616f1fca
MT
2042 }
2043
e33387d3
MT
2044 // Setup pipes for logging
2045 // INFO
2046 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_INFO, O_CLOEXEC);
2047 if (r)
2048 goto ERROR;
2049
2050 // ERROR
2051 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_ERROR, O_CLOEXEC);
2052 if (r)
2053 goto ERROR;
2054
2055#ifdef ENABLE_DEBUG
2056 // DEBUG
2057 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_DEBUG, O_CLOEXEC);
2058 if (r)
2059 goto ERROR;
2060#endif /* ENABLE_DEBUG */
2061
f949feac
MT
2062 // Configure child process
2063 struct clone_args args = {
2064 .flags =
2065 CLONE_NEWCGROUP |
2066 CLONE_NEWIPC |
2067 CLONE_NEWNS |
2068 CLONE_NEWPID |
2069 CLONE_NEWTIME |
2070 CLONE_NEWUSER |
2071 CLONE_NEWUTS |
2072 CLONE_PIDFD,
2073 .exit_signal = SIGCHLD,
2074 .pidfd = (long long unsigned int)&ctx.pidfd,
2075 };
2076
aca565fc 2077 // Launch the process in a cgroup that is a leaf of the configured cgroup
02fd4f8b 2078 if (jail->cgroup) {
f949feac
MT
2079 args.flags |= CLONE_INTO_CGROUP;
2080
ae5201c5
MT
2081 // Fetch our UUID
2082 const char* uuid = pakfire_jail_uuid(jail);
aca565fc
MT
2083
2084 // Create a temporary cgroup
ae5201c5 2085 r = pakfire_cgroup_child(&ctx.cgroup, jail->cgroup, uuid, 0);
aca565fc
MT
2086 if (r) {
2087 ERROR(jail->pakfire, "Could not create cgroup for jail: %m\n");
2088 goto ERROR;
2089 }
7bdf1d8e 2090
f949feac
MT
2091 // Clone into this cgroup
2092 args.cgroup = pakfire_cgroup_fd(ctx.cgroup);
2093 }
9b171c6a 2094
f949feac
MT
2095 // Setup networking
2096 if (!pakfire_jail_exec_has_flag(&ctx, PAKFIRE_JAIL_HAS_NETWORKING)) {
2097 args.flags |= CLONE_NEWNET;
2098 }
9b171c6a 2099
f949feac
MT
2100 // Fork this process
2101 ctx.pid = clone3(&args, sizeof(args));
2102 if (ctx.pid < 0) {
2103 ERROR(jail->pakfire, "Could not clone: %m\n");
2104 return -1;
0bd84dc1
MT
2105
2106 // Child process
f949feac
MT
2107 } else if (ctx.pid == 0) {
2108 r = pakfire_jail_child(jail, &ctx, argv);
0bd84dc1
MT
2109 _exit(r);
2110 }
2111
679ee2fa 2112 // Parent process
f949feac
MT
2113 r = pakfire_jail_parent(jail, &ctx);
2114 if (r)
2115 goto ERROR;
2116
2117 DEBUG(jail->pakfire, "Waiting for PID %d to finish its work\n", ctx.pid);
2118
2119 // Read output of the child process
d853213d
MT
2120 r = pakfire_jail_wait(jail, &ctx);
2121 if (r)
2122 goto ERROR;
0bd84dc1 2123
f949feac
MT
2124 // Handle exit status
2125 switch (ctx.status.si_code) {
2126 case CLD_EXITED:
2127 DEBUG(jail->pakfire, "The child process exited with code %d\n",
2128 ctx.status.si_status);
2129
2130 // Pass exit code
2131 exit = ctx.status.si_status;
2132 break;
2133
2134 case CLD_KILLED:
2135 ERROR(jail->pakfire, "The child process was killed\n");
2136 exit = 139;
2137 break;
2138
2139 case CLD_DUMPED:
2140 ERROR(jail->pakfire, "The child process terminated abnormally\n");
2141 break;
2142
2143 // Log anything else
2144 default:
2145 ERROR(jail->pakfire, "Unknown child exit code: %d\n", ctx.status.si_code);
2146 break;
2147 }
2148
679ee2fa 2149ERROR:
aca565fc
MT
2150 // Destroy the temporary cgroup (if any)
2151 if (ctx.cgroup) {
6b7cf275 2152 // Read cgroup stats
ec64b312
MT
2153 pakfire_cgroup_stat(ctx.cgroup, &ctx.cgroup_stats);
2154 pakfire_cgroup_stat_dump(ctx.cgroup, &ctx.cgroup_stats);
aca565fc
MT
2155 pakfire_cgroup_destroy(ctx.cgroup);
2156 pakfire_cgroup_unref(ctx.cgroup);
2157 }
2158
616f1fca 2159 // Close any file descriptors
2015cb92 2160 pakfire_jail_close_pipe(jail, ctx.pipes.stdin);
e33387d3
MT
2161 pakfire_jail_close_pipe(jail, ctx.pipes.stdout);
2162 pakfire_jail_close_pipe(jail, ctx.pipes.stderr);
f949feac
MT
2163 if (ctx.pidfd >= 0)
2164 close(ctx.pidfd);
e33387d3
MT
2165 pakfire_jail_close_pipe(jail, ctx.pipes.log_INFO);
2166 pakfire_jail_close_pipe(jail, ctx.pipes.log_ERROR);
6ac51607 2167#ifdef ENABLE_DEBUG
e33387d3 2168 pakfire_jail_close_pipe(jail, ctx.pipes.log_DEBUG);
6ac51607 2169#endif /* ENABLE_DEBUG */
8f49d254 2170 pakfire_jail_close_pipe(jail, ctx.socket);
616f1fca 2171
f949feac 2172 return exit;
9f50bf71 2173}
a45ed6b0 2174
ccdd2e95 2175PAKFIRE_EXPORT int pakfire_jail_exec(
2015cb92
MT
2176 struct pakfire_jail* jail,
2177 const char* argv[],
2178 pakfire_jail_communicate_in callback_in,
2179 pakfire_jail_communicate_out callback_out,
9fa1afb6
MT
2180 void* data, int flags) {
2181 return __pakfire_jail_exec(jail, argv, 0, callback_in, callback_out, data, flags);
2015cb92
MT
2182}
2183
db4f234f 2184static int pakfire_jail_exec_interactive(
9fa1afb6 2185 struct pakfire_jail* jail, const char* argv[], int flags) {
db4f234f
MT
2186 int r;
2187
2188 // Setup interactive stuff
2189 r = pakfire_jail_setup_interactive_env(jail);
2190 if (r)
2191 return r;
2192
9fa1afb6 2193 return __pakfire_jail_exec(jail, argv, 1, NULL, NULL, NULL, flags);
db4f234f
MT
2194}
2195
ccdd2e95
MT
2196int pakfire_jail_exec_script(struct pakfire_jail* jail,
2197 const char* script,
2198 const size_t size,
2199 const char* args[],
2200 pakfire_jail_communicate_in callback_in,
2201 pakfire_jail_communicate_out callback_out,
2202 void* data) {
a45ed6b0
MT
2203 char path[PATH_MAX];
2204 const char** argv = NULL;
35291cb7 2205 FILE* f = NULL;
a45ed6b0
MT
2206 int r;
2207
2208 const char* root = pakfire_get_path(jail->pakfire);
2209
2210 // Write the scriptlet to disk
819232d6 2211 r = pakfire_path_append(path, root, PAKFIRE_TMP_DIR "/pakfire-script.XXXXXX");
56796f84 2212 if (r)
a45ed6b0
MT
2213 goto ERROR;
2214
35291cb7
MT
2215 // Create a temporary file
2216 f = pakfire_mktemp(path, 0700);
2217 if (!f) {
2218 ERROR(jail->pakfire, "Could not create temporary file: %m\n");
a45ed6b0
MT
2219 goto ERROR;
2220 }
2221
2222 DEBUG(jail->pakfire, "Writing script to %s:\n%.*s\n", path, (int)size, script);
2223
2224 // Write data
35291cb7
MT
2225 r = fprintf(f, "%s", script);
2226 if (r < 0) {
a45ed6b0 2227 ERROR(jail->pakfire, "Could not write script to file %s: %m\n", path);
a45ed6b0
MT
2228 goto ERROR;
2229 }
2230
2231 // Close file
35291cb7 2232 r = fclose(f);
a45ed6b0
MT
2233 if (r) {
2234 ERROR(jail->pakfire, "Could not close script file %s: %m\n", path);
a45ed6b0
MT
2235 goto ERROR;
2236 }
2237
35291cb7
MT
2238 f = NULL;
2239
a45ed6b0
MT
2240 // Count how many arguments were passed
2241 unsigned int argc = 1;
2242 if (args) {
2243 for (const char** arg = args; *arg; arg++)
2244 argc++;
2245 }
2246
2247 argv = calloc(argc + 1, sizeof(*argv));
2248 if (!argv) {
2249 ERROR(jail->pakfire, "Could not allocate argv: %m\n");
2250 goto ERROR;
2251 }
2252
2253 // Set command
2254 argv[0] = (root) ? pakfire_path_relpath(root, path) : path;
2255
2256 // Copy args
2257 for (unsigned int i = 1; i < argc; i++)
2258 argv[i] = args[i-1];
2259
2260 // Run the script
9fa1afb6 2261 r = pakfire_jail_exec(jail, argv, callback_in, callback_out, data, 0);
a45ed6b0
MT
2262
2263ERROR:
2264 if (argv)
2265 free(argv);
35291cb7
MT
2266 if (f)
2267 fclose(f);
a45ed6b0
MT
2268
2269 // Remove script from disk
2270 if (*path)
2271 unlink(path);
2272
2273 return r;
2274}
82df3c77
MT
2275
2276/*
2277 A convenience function that creates a new jail, runs the given command and destroys
2278 the jail again.
2279*/
12b9b39f 2280int pakfire_jail_run(struct pakfire* pakfire, const char* argv[], int flags, char** output) {
82df3c77
MT
2281 struct pakfire_jail* jail = NULL;
2282 int r;
2283
2284 // Create a new jail
9fa1afb6 2285 r = pakfire_jail_create(&jail, pakfire);
82df3c77
MT
2286 if (r)
2287 goto ERROR;
2288
2289 // Execute the command
9fa1afb6 2290 r = pakfire_jail_exec(jail, argv, NULL, pakfire_jail_capture_stdout, output, 0);
82df3c77
MT
2291
2292ERROR:
2293 if (jail)
2294 pakfire_jail_unref(jail);
2295
2296 return r;
2297}
4f688bd8
MT
2298
2299int pakfire_jail_run_script(struct pakfire* pakfire,
49a9babc 2300 const char* script, const size_t length, const char* argv[], int flags) {
4f688bd8
MT
2301 struct pakfire_jail* jail = NULL;
2302 int r;
2303
2304 // Create a new jail
9fa1afb6 2305 r = pakfire_jail_create(&jail, pakfire);
4f688bd8
MT
2306 if (r)
2307 goto ERROR;
2308
2309 // Execute the command
49a9babc 2310 r = pakfire_jail_exec_script(jail, script, length, argv, NULL, NULL, NULL);
4f688bd8
MT
2311
2312ERROR:
2313 if (jail)
2314 pakfire_jail_unref(jail);
2315
2316 return r;
2317}
e43489f7 2318
5f6e42a2 2319int pakfire_jail_shell(struct pakfire_jail* jail) {
42a41fdf
MT
2320 int r;
2321
e43489f7
MT
2322 const char* argv[] = {
2323 "/bin/bash", "--login", NULL,
2324 };
2325
2326 // Execute /bin/bash
42a41fdf
MT
2327 r = pakfire_jail_exec_interactive(jail, argv, 0);
2328
2329 // Raise any errors
2330 if (r < 0)
2331 return r;
2332
2333 // Ignore any return codes from the shell
2334 return 0;
e43489f7
MT
2335}
2336
f7ffbb93 2337static int pakfire_jail_run_if_possible(struct pakfire* pakfire, const char** argv) {
e43489f7 2338 char path[PATH_MAX];
f7ffbb93 2339 int r;
e43489f7 2340
f7ffbb93 2341 r = pakfire_path(pakfire, path, "%s", *argv);
77e26129
MT
2342 if (r)
2343 return r;
e43489f7 2344
f7ffbb93 2345 // Check if the file is executable
e43489f7
MT
2346 r = access(path, X_OK);
2347 if (r) {
f7ffbb93 2348 DEBUG(pakfire, "%s is not executable. Skipping...\n", *argv);
e43489f7
MT
2349 return 0;
2350 }
2351
f7ffbb93
MT
2352 return pakfire_jail_run(pakfire, argv, 0, NULL);
2353}
2354
2355int pakfire_jail_ldconfig(struct pakfire* pakfire) {
e43489f7 2356 const char* argv[] = {
f7ffbb93
MT
2357 "/sbin/ldconfig",
2358 NULL,
e43489f7
MT
2359 };
2360
f7ffbb93
MT
2361 return pakfire_jail_run_if_possible(pakfire, argv);
2362}
2363
2364int pakfire_jail_run_systemd_tmpfiles(struct pakfire* pakfire) {
2365 const char* argv[] = {
2366 "/usr/bin/systemd-tmpfiles",
2367 "--create",
2368 NULL,
2369 };
2370
2371 return pakfire_jail_run_if_possible(pakfire, argv);
e43489f7 2372}