]> git.ipfire.org Git - pakfire.git/blame_incremental - src/libpakfire/jail.c
jail: Set SYSTEMD_OFFLINE when in chroot
[pakfire.git] / src / libpakfire / jail.c
... / ...
CommitLineData
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 <errno.h>
22#include <fcntl.h>
23#include <linux/capability.h>
24#include <linux/sched.h>
25#include <sys/wait.h>
26#include <linux/wait.h>
27#include <sched.h>
28#include <signal.h>
29#include <stdlib.h>
30#include <syscall.h>
31#include <sys/capability.h>
32#include <sys/epoll.h>
33#include <sys/eventfd.h>
34#include <sys/mount.h>
35#include <sys/personality.h>
36#include <sys/prctl.h>
37#include <sys/resource.h>
38#include <sys/types.h>
39#include <sys/wait.h>
40
41// libseccomp
42#include <seccomp.h>
43
44// libuuid
45#include <uuid.h>
46
47#include <pakfire/arch.h>
48#include <pakfire/cgroup.h>
49#include <pakfire/jail.h>
50#include <pakfire/logging.h>
51#include <pakfire/mount.h>
52#include <pakfire/pakfire.h>
53#include <pakfire/private.h>
54#include <pakfire/pwd.h>
55#include <pakfire/string.h>
56#include <pakfire/util.h>
57
58#define BUFFER_SIZE 1024 * 64
59#define ENVIRON_SIZE 128
60#define EPOLL_MAX_EVENTS 2
61#define MAX_MOUNTPOINTS 8
62
63// The default environment that will be set for every command
64static const struct environ {
65 const char* key;
66 const char* val;
67} ENV[] = {
68 { "HOME", "/root" },
69 { "LANG", "C.utf-8" },
70 { "PATH", "/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin", },
71 { "TERM", "vt100" },
72 { NULL, NULL },
73};
74
75struct pakfire_jail_mountpoint {
76 char source[PATH_MAX];
77 char target[PATH_MAX];
78 int flags;
79};
80
81struct pakfire_jail {
82 struct pakfire* pakfire;
83 int nrefs;
84
85 // A unique ID for each jail
86 uuid_t uuid;
87 char __uuid[UUID_STR_LEN];
88
89 // Flags
90 int flags;
91
92 // Resource Limits
93 int nice;
94
95 // CGroup
96 struct pakfire_cgroup* cgroup;
97
98 // Environment
99 char* env[ENVIRON_SIZE];
100
101 // Mountpoints
102 struct pakfire_jail_mountpoint mountpoints[MAX_MOUNTPOINTS];
103 unsigned int num_mountpoints;
104};
105
106struct pakfire_log_buffer {
107 char data[BUFFER_SIZE];
108 size_t used;
109};
110
111enum pakfire_jail_exec_flags {
112 PAKFIRE_JAIL_HAS_NETWORKING = (1 << 0),
113};
114
115struct pakfire_jail_exec {
116 int flags;
117
118 // PID (of the child)
119 pid_t pid;
120 int pidfd;
121
122 // Process status (from waitid)
123 siginfo_t status;
124
125 // FD to notify the client that the parent has finished initialization
126 int completed_fd;
127
128 // Log pipes
129 struct pakfire_jail_pipes {
130 int stdin[2];
131 int stdout[2];
132 int stderr[2];
133
134 // Logging
135 int log_INFO[2];
136 int log_ERROR[2];
137 int log_DEBUG[2];
138 } pipes;
139
140 // Communicate
141 struct pakfire_jail_communicate {
142 pakfire_jail_communicate_in in;
143 pakfire_jail_communicate_out out;
144 void* data;
145 } communicate;
146
147 // Log buffers
148 struct pakfire_jail_buffers {
149 struct pakfire_log_buffer stdout;
150 struct pakfire_log_buffer stderr;
151
152 // Logging
153 struct pakfire_log_buffer log_INFO;
154 struct pakfire_log_buffer log_ERROR;
155 struct pakfire_log_buffer log_DEBUG;
156 } buffers;
157
158 struct pakfire_cgroup* cgroup;
159 struct pakfire_cgroup_stats cgroup_stats;
160};
161
162static int clone3(struct clone_args* args, size_t size) {
163 return syscall(__NR_clone3, args, size);
164}
165
166static int pakfire_jail_exec_has_flag(
167 const struct pakfire_jail_exec* ctx, const enum pakfire_jail_exec_flags flag) {
168 return ctx->flags & flag;
169}
170
171static void pakfire_jail_free(struct pakfire_jail* jail) {
172 DEBUG(jail->pakfire, "Freeing jail at %p\n", jail);
173
174 // Free environment
175 for (unsigned int i = 0; jail->env[i]; i++)
176 free(jail->env[i]);
177
178 if (jail->cgroup)
179 pakfire_cgroup_unref(jail->cgroup);
180
181 pakfire_unref(jail->pakfire);
182 free(jail);
183}
184
185/*
186 Passes any log messages on to the default pakfire log callback
187*/
188static int pakfire_jail_default_log_callback(struct pakfire* pakfire, void* data,
189 int priority, const char* line, size_t length) {
190 switch (priority) {
191 case LOG_INFO:
192 INFO(pakfire, "%s", line);
193 break;
194
195 case LOG_ERR:
196 ERROR(pakfire, "%s", line);
197 break;
198
199#ifdef ENABLE_DEBUG
200 case LOG_DEBUG:
201 DEBUG(pakfire, "%s", line);
202 break;
203#endif
204 }
205
206 return 0;
207}
208
209static int pakfire_jail_setup_interactive_env(struct pakfire_jail* jail) {
210 // Set PS1
211 int r = pakfire_jail_set_env(jail, "PS1", "pakfire-jail \\w> ");
212 if (r)
213 return r;
214
215 // Copy TERM
216 char* TERM = secure_getenv("TERM");
217 if (TERM) {
218 r = pakfire_jail_set_env(jail, "TERM", TERM);
219 if (r)
220 return r;
221 }
222
223 // Copy LANG
224 char* LANG = secure_getenv("LANG");
225 if (LANG) {
226 r = pakfire_jail_set_env(jail, "LANG", LANG);
227 if (r)
228 return r;
229 }
230
231 return 0;
232}
233
234PAKFIRE_EXPORT int pakfire_jail_create(struct pakfire_jail** jail,
235 struct pakfire* pakfire, int flags) {
236 int r;
237
238 // Allocate a new jail
239 struct pakfire_jail* j = calloc(1, sizeof(*j));
240 if (!j)
241 return 1;
242
243 // Reference Pakfire
244 j->pakfire = pakfire_ref(pakfire);
245
246 // Initialize reference counter
247 j->nrefs = 1;
248
249 // Store flags
250 j->flags = flags;
251
252 // Generate a random UUID
253 uuid_generate_random(j->uuid);
254
255 DEBUG(j->pakfire, "Allocated new jail at %p\n", j);
256
257 // Set default environment
258 for (const struct environ* e = ENV; e->key; e++) {
259 r = pakfire_jail_set_env(j, e->key, e->val);
260 if (r)
261 goto ERROR;
262 }
263
264 // Disable systemctl to talk to systemd
265 if (!pakfire_on_root(j->pakfire)) {
266 r = pakfire_jail_set_env(j, "SYSTEMD_OFFLINE", "1");
267 if (r)
268 goto ERROR;
269 }
270
271 // Done
272 *jail = j;
273 return 0;
274
275ERROR:
276 pakfire_jail_free(j);
277
278 return r;
279}
280
281PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_ref(struct pakfire_jail* jail) {
282 ++jail->nrefs;
283
284 return jail;
285}
286
287PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_unref(struct pakfire_jail* jail) {
288 if (--jail->nrefs > 0)
289 return jail;
290
291 pakfire_jail_free(jail);
292 return NULL;
293}
294
295static const char* pakfire_jail_uuid(struct pakfire_jail* jail) {
296 if (!*jail->__uuid)
297 uuid_unparse_lower(jail->uuid, jail->__uuid);
298
299 return jail->__uuid;
300}
301
302// Resource Limits
303
304PAKFIRE_EXPORT int pakfire_jail_nice(struct pakfire_jail* jail, int nice) {
305 // Check if nice level is in range
306 if (nice < -19 || nice > 20) {
307 errno = EINVAL;
308 return 1;
309 }
310
311 // Store nice level
312 jail->nice = nice;
313
314 return 0;
315}
316
317int pakfire_jail_set_cgroup(struct pakfire_jail* jail, struct pakfire_cgroup* cgroup) {
318 // Free any previous cgroup
319 if (jail->cgroup) {
320 pakfire_cgroup_unref(jail->cgroup);
321 jail->cgroup = NULL;
322 }
323
324 // Set any new cgroup
325 if (cgroup) {
326 DEBUG(jail->pakfire, "Setting cgroup %p\n", cgroup);
327
328 jail->cgroup = pakfire_cgroup_ref(cgroup);
329 }
330
331 // Done
332 return 0;
333}
334
335// Environment
336
337// Returns the length of the environment
338static unsigned int pakfire_jail_env_length(struct pakfire_jail* jail) {
339 unsigned int i = 0;
340
341 // Count everything in the environment
342 for (char** e = jail->env; *e; e++)
343 i++;
344
345 return i;
346}
347
348// Finds an existing environment variable and returns its index or -1 if not found
349static int pakfire_jail_find_env(struct pakfire_jail* jail, const char* key) {
350 if (!key) {
351 errno = EINVAL;
352 return -1;
353 }
354
355 char buffer[strlen(key) + 2];
356 pakfire_string_format(buffer, "%s=", key);
357
358 for (unsigned int i = 0; jail->env[i]; i++) {
359 if (pakfire_string_startswith(jail->env[i], buffer))
360 return i;
361 }
362
363 // Nothing found
364 return -1;
365}
366
367// Returns the value of an environment variable or NULL
368PAKFIRE_EXPORT const char* pakfire_jail_get_env(struct pakfire_jail* jail,
369 const char* key) {
370 int i = pakfire_jail_find_env(jail, key);
371 if (i < 0)
372 return NULL;
373
374 return jail->env[i] + strlen(key) + 1;
375}
376
377// Sets an environment variable
378PAKFIRE_EXPORT int pakfire_jail_set_env(struct pakfire_jail* jail,
379 const char* key, const char* value) {
380 // Find the index where to write this value to
381 int i = pakfire_jail_find_env(jail, key);
382 if (i < 0)
383 i = pakfire_jail_env_length(jail);
384
385 // Return -ENOSPC when the environment is full
386 if (i >= ENVIRON_SIZE) {
387 errno = ENOSPC;
388 return -1;
389 }
390
391 // Free any previous value
392 if (jail->env[i])
393 free(jail->env[i]);
394
395 // Format and set environment variable
396 asprintf(&jail->env[i], "%s=%s", key, value);
397
398 DEBUG(jail->pakfire, "Set environment variable: %s\n", jail->env[i]);
399
400 return 0;
401}
402
403// Imports an environment
404PAKFIRE_EXPORT int pakfire_jail_import_env(struct pakfire_jail* jail, const char* env[]) {
405 if (!env)
406 return 0;
407
408 char* key;
409 char* val;
410 int r;
411
412 // Copy environment variables
413 for (unsigned int i = 0; env[i]; i++) {
414 r = pakfire_string_partition(env[i], "=", &key, &val);
415 if (r)
416 continue;
417
418 // Set value
419 r = pakfire_jail_set_env(jail, key, val);
420
421 if (key)
422 free(key);
423 if (val)
424 free(val);
425
426 // Break on error
427 if (r)
428 return r;
429 }
430
431 return 0;
432}
433
434/*
435 This function replaces any logging in the child process.
436
437 All log messages will be sent to the parent process through their respective pipes.
438*/
439static void pakfire_jail_log(void* data, int priority, const char* file,
440 int line, const char* fn, const char* format, va_list args) {
441 struct pakfire_jail_pipes* pipes = (struct pakfire_jail_pipes*)data;
442 int fd;
443
444 switch (priority) {
445 case LOG_INFO:
446 fd = pipes->log_INFO[1];
447 break;
448
449 case LOG_ERR:
450 fd = pipes->log_ERROR[1];
451 break;
452
453#ifdef ENABLE_DEBUG
454 case LOG_DEBUG:
455 fd = pipes->log_DEBUG[1];
456 break;
457#endif /* ENABLE_DEBUG */
458
459 // Ignore any messages of an unknown priority
460 default:
461 return;
462 }
463
464 // Send the log message
465 if (fd)
466 vdprintf(fd, format, args);
467}
468
469static int pakfire_jail_log_buffer_is_full(const struct pakfire_log_buffer* buffer) {
470 return (sizeof(buffer->data) == buffer->used);
471}
472
473/*
474 This function reads as much data as it can from the file descriptor.
475 If it finds a whole line in it, it will send it to the logger and repeat the process.
476 If not newline character is found, it will try to read more data until it finds one.
477*/
478static int pakfire_jail_handle_log(struct pakfire_jail* jail,
479 struct pakfire_jail_exec* ctx, int priority, int fd,
480 struct pakfire_log_buffer* buffer, pakfire_jail_communicate_out callback, void* data) {
481 char line[BUFFER_SIZE + 1];
482
483 // Fill up buffer from fd
484 if (buffer->used < sizeof(buffer->data)) {
485 ssize_t bytes_read = read(fd, buffer->data + buffer->used,
486 sizeof(buffer->data) - buffer->used);
487
488 // Handle errors
489 if (bytes_read < 0) {
490 ERROR(jail->pakfire, "Could not read from fd %d: %m\n", fd);
491 return -1;
492 }
493
494 // Update buffer size
495 buffer->used += bytes_read;
496 }
497
498 // See if we have any lines that we can write
499 while (buffer->used) {
500 // Search for the end of the first line
501 char* eol = memchr(buffer->data, '\n', buffer->used);
502
503 // No newline found
504 if (!eol) {
505 // If the buffer is full, we send the content to the logger and try again
506 // This should not happen in practise
507 if (pakfire_jail_log_buffer_is_full(buffer)) {
508 DEBUG(jail->pakfire, "Logging buffer is full. Sending all content\n");
509
510 eol = buffer->data + sizeof(buffer->data) - 1;
511
512 // Otherwise we might have only read parts of the output
513 } else
514 break;
515 }
516
517 // Find the length of the string
518 size_t length = eol - buffer->data + 1;
519
520 // Copy the line into the buffer
521 memcpy(line, buffer->data, length);
522
523 // Terminate the string
524 line[length] = '\0';
525
526 // Log the line
527 if (callback) {
528 int r = callback(jail->pakfire, data, priority, line, length);
529 if (r) {
530 ERROR(jail->pakfire, "The logging callback returned an error: %d\n", r);
531 return r;
532 }
533 }
534
535 // Remove line from buffer
536 memmove(buffer->data, buffer->data + length, buffer->used - length);
537 buffer->used -= length;
538 }
539
540 return 0;
541}
542
543static int pakfire_jail_stream_stdin(struct pakfire_jail* jail,
544 struct pakfire_jail_exec* ctx, const int fd) {
545 int r;
546
547 // Nothing to do if there is no stdin callback set
548 if (!ctx->communicate.in) {
549 DEBUG(jail->pakfire, "Callback for standard input is not set\n");
550 return 0;
551 }
552
553 // Skip if the writing pipe has already been closed
554 if (!ctx->pipes.stdin[1])
555 return 0;
556
557 DEBUG(jail->pakfire, "Streaming standard input...\n");
558
559 // Calling the callback
560 r = ctx->communicate.in(jail->pakfire, ctx->communicate.data, fd);
561
562 DEBUG(jail->pakfire, "Standard input callback finished: %d\n", r);
563
564 // The callback signaled that it has written everything
565 if (r == EOF) {
566 DEBUG(jail->pakfire, "Closing standard input pipe\n");
567
568 // Close the file-descriptor
569 close(fd);
570
571 // Reset the file-descriptor so it won't be closed again later
572 ctx->pipes.stdin[1] = 0;
573
574 // Report success
575 r = 0;
576 }
577
578 return r;
579}
580
581static int pakfire_jail_setup_pipe(struct pakfire_jail* jail, int (*fds)[2], const int flags) {
582 int r = pipe2(*fds, flags);
583 if (r < 0) {
584 ERROR(jail->pakfire, "Could not setup pipe: %m\n");
585 return 1;
586 }
587
588 return 0;
589}
590
591static void pakfire_jail_close_pipe(struct pakfire_jail* jail, int fds[2]) {
592 for (unsigned int i = 0; i < 2; i++)
593 if (fds[i])
594 close(fds[i]);
595}
596
597/*
598 This is a convenience function to fetch the reading end of a pipe and
599 closes the write end.
600*/
601static int pakfire_jail_get_pipe_to_read(struct pakfire_jail* jail, int (*fds)[2]) {
602 // Give the variables easier names to avoid confusion
603 int* fd_read = &(*fds)[0];
604 int* fd_write = &(*fds)[1];
605
606 // Close the write end of the pipe
607 if (*fd_write) {
608 close(*fd_write);
609 *fd_write = 0;
610 }
611
612 // Return the read end
613 return *fd_read;
614}
615
616static int pakfire_jail_get_pipe_to_write(struct pakfire_jail* jail, int (*fds)[2]) {
617 // Give the variables easier names to avoid confusion
618 int* fd_read = &(*fds)[0];
619 int* fd_write = &(*fds)[1];
620
621 // Close the read end of the pipe
622 if (*fd_read) {
623 close(*fd_read);
624 *fd_read = 0;
625 }
626
627 // Return the write end
628 return *fd_write;
629}
630
631static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
632 int epollfd = -1;
633 struct epoll_event ev;
634 struct epoll_event events[EPOLL_MAX_EVENTS];
635 int r = 0;
636
637 // Fetch file descriptors from context
638 const int stdin = pakfire_jail_get_pipe_to_write(jail, &ctx->pipes.stdin);
639 const int stdout = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stdout);
640 const int stderr = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stderr);
641 const int pidfd = ctx->pidfd;
642
643 // Logging
644 const int log_INFO = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_INFO);
645 const int log_ERROR = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_ERROR);
646 const int log_DEBUG = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_DEBUG);
647
648 // Make a list of all file descriptors we are interested in
649 int fds[] = {
650 stdin, stdout, stderr, pidfd, log_INFO, log_ERROR, log_DEBUG,
651 };
652
653 // Setup epoll
654 epollfd = epoll_create1(0);
655 if (epollfd < 0) {
656 ERROR(jail->pakfire, "Could not initialize epoll(): %m\n");
657 r = 1;
658 goto ERROR;
659 }
660
661 // Turn file descriptors into non-blocking mode and add them to epoll()
662 for (unsigned int i = 0; i < sizeof(fds) / sizeof(*fds); i++) {
663 int fd = fds[i];
664
665 // Skip fds which were not initialized
666 if (fd <= 0)
667 continue;
668
669 ev.events = EPOLLHUP;
670
671 if (fd == stdin)
672 ev.events |= EPOLLOUT;
673 else
674 ev.events |= EPOLLIN;
675
676 // Read flags
677 int flags = fcntl(fd, F_GETFL, 0);
678
679 // Set modified flags
680 if (fcntl(fd, F_SETFL, flags|O_NONBLOCK) < 0) {
681 ERROR(jail->pakfire,
682 "Could not set file descriptor %d into non-blocking mode: %m\n", fd);
683 r = 1;
684 goto ERROR;
685 }
686
687 ev.data.fd = fd;
688
689 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
690 ERROR(jail->pakfire, "Could not add file descriptor %d to epoll(): %m\n", fd);
691 r = 1;
692 goto ERROR;
693 }
694 }
695
696 int ended = 0;
697
698 // Loop for as long as the process is alive
699 while (!ended) {
700 int num = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, -1);
701 if (num < 1) {
702 // Ignore if epoll_wait() has been interrupted
703 if (errno == EINTR)
704 continue;
705
706 ERROR(jail->pakfire, "epoll_wait() failed: %m\n");
707 r = 1;
708
709 goto ERROR;
710 }
711
712 for (int i = 0; i < num; i++) {
713 int e = events[i].events;
714 int fd = events[i].data.fd;
715
716 struct pakfire_log_buffer* buffer = NULL;
717 pakfire_jail_communicate_out callback = NULL;
718 void* data = NULL;
719 int priority;
720
721 // Check if there is any data to be read
722 if (e & EPOLLIN) {
723 // Handle any changes to the PIDFD
724 if (fd == pidfd) {
725 // Call waidid() and store the result
726 r = waitid(P_PIDFD, ctx->pidfd, &ctx->status, WEXITED);
727 if (r) {
728 ERROR(jail->pakfire, "waitid() failed: %m\n");
729 goto ERROR;
730 }
731
732 // Mark that we have ended so that we will process the remaining
733 // events from epoll() now, but won't restart the outer loop.
734 ended = 1;
735 continue;
736
737 // Handle logging messages
738 } else if (fd == log_INFO) {
739 buffer = &ctx->buffers.log_INFO;
740 priority = LOG_INFO;
741
742 callback = pakfire_jail_default_log_callback;
743
744 } else if (fd == log_ERROR) {
745 buffer = &ctx->buffers.log_ERROR;
746 priority = LOG_ERR;
747
748 callback = pakfire_jail_default_log_callback;
749
750 } else if (fd == log_DEBUG) {
751 buffer = &ctx->buffers.log_DEBUG;
752 priority = LOG_DEBUG;
753
754 callback = pakfire_jail_default_log_callback;
755
756 // Handle anything from the log pipes
757 } else if (fd == stdout) {
758 buffer = &ctx->buffers.stdout;
759 priority = LOG_INFO;
760
761 callback = ctx->communicate.out;
762 data = ctx->communicate.data;
763
764 } else if (fd == stderr) {
765 buffer = &ctx->buffers.stderr;
766 priority = LOG_ERR;
767
768 callback = ctx->communicate.out;
769 data = ctx->communicate.data;
770
771 } else {
772 DEBUG(jail->pakfire, "Received invalid file descriptor %d\n", fd);
773 continue;
774 }
775
776 // Handle log event
777 r = pakfire_jail_handle_log(jail, ctx, priority, fd, buffer, callback, data);
778 if (r)
779 goto ERROR;
780 }
781
782 if (e & EPOLLOUT) {
783 // Handle standard input
784 if (fd == stdin) {
785 r = pakfire_jail_stream_stdin(jail, ctx, fd);
786 if (r) {
787 switch (errno) {
788 // Ignore if we filled up the buffer
789 case EAGAIN:
790 break;
791
792 default:
793 ERROR(jail->pakfire, "Could not write to stdin: %m\n");
794 goto ERROR;
795 }
796 }
797 }
798 }
799
800 // Check if any file descriptors have been closed
801 if (e & EPOLLHUP) {
802 // Remove the file descriptor
803 r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
804 if (r) {
805 ERROR(jail->pakfire, "Could not remove closed file-descriptor %d: %m\n", fd);
806 goto ERROR;
807 }
808 }
809 }
810 }
811
812ERROR:
813 if (epollfd > 0)
814 close(epollfd);
815
816 return r;
817}
818
819int pakfire_jail_capture_stdout(struct pakfire* pakfire, void* data,
820 int priority, const char* line, size_t length) {
821 char** output = (char**)data;
822 int r;
823
824 // Append everything from stdout to a buffer
825 if (output && priority == LOG_INFO) {
826 r = asprintf(output, "%s%s", (output && *output) ? *output : "", line);
827 if (r < 0)
828 return 1;
829 return 0;
830 }
831
832 // Send everything else to the default logger
833 return pakfire_jail_default_log_callback(pakfire, NULL, priority, line, length);
834}
835
836// Capabilities
837
838static int pakfire_jail_drop_capabilities(struct pakfire_jail* jail) {
839 const int capabilities[] = {
840 // Deny access to the kernel's audit system
841 CAP_AUDIT_CONTROL,
842 CAP_AUDIT_READ,
843 CAP_AUDIT_WRITE,
844
845 // Deny suspending block devices
846 CAP_BLOCK_SUSPEND,
847
848 // Deny any stuff with BPF
849 CAP_BPF,
850
851 // Deny checkpoint restore
852 CAP_CHECKPOINT_RESTORE,
853
854 // Deny opening files by inode number (open_by_handle_at)
855 CAP_DAC_READ_SEARCH,
856
857 // Deny setting SUID bits
858 CAP_FSETID,
859
860 // Deny locking more memory
861 CAP_IPC_LOCK,
862
863 // Deny modifying any Apparmor/SELinux/SMACK configuration
864 CAP_MAC_ADMIN,
865 CAP_MAC_OVERRIDE,
866
867 // Deny creating any special devices
868 CAP_MKNOD,
869
870 // Deny setting any capabilities
871 CAP_SETFCAP,
872
873 // Deny reading from syslog
874 CAP_SYSLOG,
875
876 // Deny any admin actions (mount, sethostname, ...)
877 CAP_SYS_ADMIN,
878
879 // Deny rebooting the system
880 CAP_SYS_BOOT,
881
882 // Deny loading kernel modules
883 CAP_SYS_MODULE,
884
885 // Deny setting nice level
886 CAP_SYS_NICE,
887
888 // Deny access to /proc/kcore, /dev/mem, /dev/kmem
889 CAP_SYS_RAWIO,
890
891 // Deny circumventing any resource limits
892 CAP_SYS_RESOURCE,
893
894 // Deny setting the system time
895 CAP_SYS_TIME,
896
897 // Deny playing with suspend
898 CAP_WAKE_ALARM,
899
900 0,
901 };
902
903 DEBUG(jail->pakfire, "Dropping capabilities...\n");
904
905 size_t num_caps = 0;
906 int r;
907
908 // Drop any capabilities
909 for (const int* cap = capabilities; *cap; cap++) {
910 r = prctl(PR_CAPBSET_DROP, *cap, 0, 0, 0);
911 if (r) {
912 ERROR(jail->pakfire, "Could not drop capability %d: %m\n", *cap);
913 return r;
914 }
915
916 num_caps++;
917 }
918
919 // Fetch any capabilities
920 cap_t caps = cap_get_proc();
921 if (!caps) {
922 ERROR(jail->pakfire, "Could not read capabilities: %m\n");
923 return 1;
924 }
925
926 /*
927 Set inheritable capabilities
928
929 This ensures that no processes will be able to gain any of the listed
930 capabilities again.
931 */
932 r = cap_set_flag(caps, CAP_INHERITABLE, num_caps, capabilities, CAP_CLEAR);
933 if (r) {
934 ERROR(jail->pakfire, "cap_set_flag() failed: %m\n");
935 goto ERROR;
936 }
937
938 // Restore capabilities
939 r = cap_set_proc(caps);
940 if (r) {
941 ERROR(jail->pakfire, "Could not restore capabilities: %m\n");
942 goto ERROR;
943 }
944
945ERROR:
946 if (caps)
947 cap_free(caps);
948
949 return r;
950}
951
952// Syscall Filter
953
954static int pakfire_jail_limit_syscalls(struct pakfire_jail* jail) {
955 const int syscalls[] = {
956 // The kernel's keyring isn't namespaced
957 SCMP_SYS(keyctl),
958 SCMP_SYS(add_key),
959 SCMP_SYS(request_key),
960
961 // Disable userfaultfd
962 SCMP_SYS(userfaultfd),
963
964 // Disable perf which could leak a lot of information about the host
965 SCMP_SYS(perf_event_open),
966
967 0,
968 };
969 int r = 1;
970
971 DEBUG(jail->pakfire, "Applying syscall filter...\n");
972
973 // Setup a syscall filter which allows everything by default
974 scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
975 if (!ctx) {
976 ERROR(jail->pakfire, "Could not setup seccomp filter: %m\n");
977 goto ERROR;
978 }
979
980 // All all syscalls
981 for (const int* syscall = syscalls; *syscall; syscall++) {
982 r = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), *syscall, 0);
983 if (r) {
984 ERROR(jail->pakfire, "Could not configure syscall %d: %m\n", *syscall);
985 goto ERROR;
986 }
987 }
988
989 // Load syscall filter into the kernel
990 r = seccomp_load(ctx);
991 if (r) {
992 ERROR(jail->pakfire, "Could not load syscall filter into the kernel: %m\n");
993 goto ERROR;
994 }
995
996ERROR:
997 if (ctx)
998 seccomp_release(ctx);
999
1000 return r;
1001}
1002
1003// Mountpoints
1004
1005PAKFIRE_EXPORT int pakfire_jail_bind(struct pakfire_jail* jail,
1006 const char* source, const char* target, int flags) {
1007 struct pakfire_jail_mountpoint* mp = NULL;
1008 int r;
1009
1010 // Check if there is any space left
1011 if (jail->num_mountpoints >= MAX_MOUNTPOINTS) {
1012 errno = ENOSPC;
1013 return 1;
1014 }
1015
1016 // Check for valid inputs
1017 if (!source || !target) {
1018 errno = EINVAL;
1019 return 1;
1020 }
1021
1022 // Select the next free slot
1023 mp = &jail->mountpoints[jail->num_mountpoints];
1024
1025 // Copy source
1026 r = pakfire_string_set(mp->source, source);
1027 if (r) {
1028 ERROR(jail->pakfire, "Could not copy source: %m\n");
1029 return r;
1030 }
1031
1032 // Copy target
1033 r = pakfire_string_set(mp->target, target);
1034 if (r) {
1035 ERROR(jail->pakfire, "Could not copy target: %m\n");
1036 return r;
1037 }
1038
1039 // Copy flags
1040 mp->flags = flags;
1041
1042 // Increment counter
1043 jail->num_mountpoints++;
1044
1045 return 0;
1046}
1047
1048static int pakfire_jail_mount_networking(struct pakfire_jail* jail) {
1049 int r;
1050
1051 const char* paths[] = {
1052 "/etc/hosts",
1053 "/etc/resolv.conf",
1054 NULL,
1055 };
1056
1057 // Bind-mount all paths read-only
1058 for (const char** path = paths; *path; path++) {
1059 r = pakfire_bind(jail->pakfire, *path, NULL, MS_RDONLY);
1060 if (r)
1061 return r;
1062 }
1063
1064 return 0;
1065}
1066
1067/*
1068 Mounts everything that we require in the new namespace
1069*/
1070static int pakfire_jail_mount(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
1071 struct pakfire_jail_mountpoint* mp = NULL;
1072 int r;
1073
1074 // Mount all default stuff
1075 r = pakfire_mount_all(jail->pakfire);
1076 if (r)
1077 return r;
1078
1079 // Mount networking stuff
1080 if (pakfire_jail_exec_has_flag(ctx, PAKFIRE_JAIL_HAS_NETWORKING)) {
1081 r = pakfire_jail_mount_networking(jail);
1082 if (r)
1083 return r;
1084 }
1085
1086 // Mount all custom stuff
1087 for (unsigned int i = 0; i < jail->num_mountpoints; i++) {
1088 // Fetch mountpoint
1089 mp = &jail->mountpoints[i];
1090
1091 // Mount it
1092 r = pakfire_bind(jail->pakfire, mp->source, mp->target, mp->flags);
1093 if (r)
1094 return r;
1095 }
1096
1097 // Log all mountpoints
1098 pakfire_mount_list(jail->pakfire);
1099
1100 return 0;
1101}
1102
1103// UID/GID Mapping
1104
1105static int pakfire_jail_setup_uid_mapping(struct pakfire_jail* jail, pid_t pid) {
1106 char path[PATH_MAX];
1107 int r;
1108
1109 // Skip mapping anything when running on /
1110 if (pakfire_on_root(jail->pakfire))
1111 return 0;
1112
1113 // Make path
1114 r = pakfire_string_format(path, "/proc/%d/uid_map", pid);
1115 if (r)
1116 return r;
1117
1118 // Fetch UID
1119 const uid_t uid = pakfire_uid(jail->pakfire);
1120
1121 // Fetch SUBUID
1122 const struct pakfire_subid* subuid = pakfire_subuid(jail->pakfire);
1123 if (!subuid)
1124 return 1;
1125
1126 /* When running as root, we will map the entire range.
1127
1128 When running as a non-privileged user, we will map the root user inside the jail
1129 to the user's UID outside of the jail, and we will map the rest starting from one.
1130 */
1131
1132 // Running as root
1133 if (uid == 0) {
1134 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
1135 "0 %lu %lu\n", subuid->id, subuid->length);
1136 } else {
1137 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
1138 "0 %lu 1\n1 %lu %lu\n", uid, subuid->id, subuid->length);
1139 }
1140
1141 if (r) {
1142 ERROR(jail->pakfire, "Could not map UIDs: %m\n");
1143 return r;
1144 }
1145
1146 return r;
1147}
1148
1149static int pakfire_jail_setup_gid_mapping(struct pakfire_jail* jail, pid_t pid) {
1150 char path[PATH_MAX];
1151 int r;
1152
1153 // Skip mapping anything when running on /
1154 if (pakfire_on_root(jail->pakfire))
1155 return 0;
1156
1157 // Fetch GID
1158 const gid_t gid = pakfire_gid(jail->pakfire);
1159
1160 // Fetch SUBGID
1161 const struct pakfire_subid* subgid = pakfire_subgid(jail->pakfire);
1162 if (!subgid)
1163 return 1;
1164
1165 // Make path
1166 r = pakfire_string_format(path, "/proc/%d/gid_map", pid);
1167 if (r)
1168 return r;
1169
1170 // Running as root
1171 if (gid == 0) {
1172 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
1173 "0 %lu %lu\n", subgid->id, subgid->length);
1174 } else {
1175 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
1176 "0 %lu 1\n%1 %lu %lu\n", gid, subgid->id, subgid->length);
1177 }
1178
1179 if (r) {
1180 ERROR(jail->pakfire, "Could not map GIDs: %m\n");
1181 return r;
1182 }
1183
1184 return r;
1185}
1186
1187static int pakfire_jail_setgroups(struct pakfire_jail* jail, pid_t pid) {
1188 char path[PATH_MAX];
1189 int r = 1;
1190
1191 // Make path
1192 r = pakfire_string_format(path, "/proc/%d/setgroups", pid);
1193 if (r)
1194 return r;
1195
1196 // Open file for writing
1197 FILE* f = fopen(path, "w");
1198 if (!f) {
1199 ERROR(jail->pakfire, "Could not open %s for writing: %m\n", path);
1200 goto ERROR;
1201 }
1202
1203 // Write content
1204 int bytes_written = fprintf(f, "deny\n");
1205 if (bytes_written <= 0) {
1206 ERROR(jail->pakfire, "Could not write to %s: %m\n", path);
1207 goto ERROR;
1208 }
1209
1210 r = fclose(f);
1211 f = NULL;
1212 if (r) {
1213 ERROR(jail->pakfire, "Could not close %s: %m\n", path);
1214 goto ERROR;
1215 }
1216
1217ERROR:
1218 if (f)
1219 fclose(f);
1220
1221 return r;
1222}
1223
1224static int pakfire_jail_send_signal(struct pakfire_jail* jail, int fd) {
1225 const uint64_t val = 1;
1226 int r = 0;
1227
1228 DEBUG(jail->pakfire, "Sending signal...\n");
1229
1230 // Write to the file descriptor
1231 ssize_t bytes_written = write(fd, &val, sizeof(val));
1232 if (bytes_written < 0 || (size_t)bytes_written < sizeof(val)) {
1233 ERROR(jail->pakfire, "Could not send signal: %m\n");
1234 r = 1;
1235 }
1236
1237 // Close the file descriptor
1238 close(fd);
1239
1240 return r;
1241}
1242
1243static int pakfire_jail_wait_for_signal(struct pakfire_jail* jail, int fd) {
1244 uint64_t val = 0;
1245 int r = 0;
1246
1247 DEBUG(jail->pakfire, "Waiting for signal...\n");
1248
1249 ssize_t bytes_read = read(fd, &val, sizeof(val));
1250 if (bytes_read < 0 || (size_t)bytes_read < sizeof(val)) {
1251 ERROR(jail->pakfire, "Error waiting for signal: %m\n");
1252 r = 1;
1253 }
1254
1255 // Close the file descriptor
1256 close(fd);
1257
1258 return r;
1259}
1260
1261/*
1262 Performs the initialisation that needs to happen in the parent part
1263*/
1264static int pakfire_jail_parent(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
1265 int r;
1266
1267 // Setup UID mapping
1268 r = pakfire_jail_setup_uid_mapping(jail, ctx->pid);
1269 if (r)
1270 return r;
1271
1272 // Write "deny" to /proc/PID/setgroups
1273 r = pakfire_jail_setgroups(jail, ctx->pid);
1274 if (r)
1275 return r;
1276
1277 // Setup GID mapping
1278 r = pakfire_jail_setup_gid_mapping(jail, ctx->pid);
1279 if (r)
1280 return r;
1281
1282 // Parent has finished initialisation
1283 DEBUG(jail->pakfire, "Parent has finished initialization\n");
1284
1285 // Send signal to client
1286 r = pakfire_jail_send_signal(jail, ctx->completed_fd);
1287 if (r)
1288 return r;
1289
1290 return 0;
1291}
1292
1293static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
1294 const char* argv[]) {
1295 int r;
1296
1297 // Redirect any logging to our log pipe
1298 pakfire_set_log_callback(jail->pakfire, pakfire_jail_log, &ctx->pipes);
1299
1300 // Die with parent
1301 r = prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
1302 if (r) {
1303 ERROR(jail->pakfire, "Could not configure to die with parent: %m\n");
1304 return 126;
1305 }
1306
1307 // Fetch my own PID
1308 pid_t pid = getpid();
1309
1310 DEBUG(jail->pakfire, "Launched child process in jail with PID %d\n", pid);
1311
1312 // Wait for the parent to finish initialization
1313 r = pakfire_jail_wait_for_signal(jail, ctx->completed_fd);
1314 if (r)
1315 return r;
1316
1317 // Perform further initialization
1318
1319 // Fetch UID/GID
1320 uid_t uid = getuid();
1321 gid_t gid = getgid();
1322
1323 // Fetch EUID/EGID
1324 uid_t euid = geteuid();
1325 gid_t egid = getegid();
1326
1327 DEBUG(jail->pakfire, " UID: %d (effective %d)\n", uid, euid);
1328 DEBUG(jail->pakfire, " GID: %d (effective %d)\n", gid, egid);
1329
1330 // Check if we are (effectively running as root)
1331 if (uid || gid || euid || egid) {
1332 ERROR(jail->pakfire, "Child process is not running as root\n");
1333 return 126;
1334 }
1335
1336 const char* root = pakfire_get_path(jail->pakfire);
1337 const char* arch = pakfire_get_arch(jail->pakfire);
1338
1339 // Change root (unless root is /)
1340 if (!pakfire_on_root(jail->pakfire)) {
1341 // Mount everything
1342 r = pakfire_jail_mount(jail, ctx);
1343 if (r)
1344 return r;
1345
1346 // Call chroot()
1347 r = chroot(root);
1348 if (r) {
1349 ERROR(jail->pakfire, "chroot() to %s failed: %m\n", root);
1350 return 1;
1351 }
1352
1353 // Change directory to /
1354 r = chdir("/");
1355 if (r) {
1356 ERROR(jail->pakfire, "chdir() after chroot() failed: %m\n");
1357 return 1;
1358 }
1359 }
1360
1361 // Set personality
1362 unsigned long persona = pakfire_arch_personality(arch);
1363 if (persona) {
1364 r = personality(persona);
1365 if (r < 0) {
1366 ERROR(jail->pakfire, "Could not set personality (%x)\n", (unsigned int)persona);
1367 return 1;
1368 }
1369 }
1370
1371 // Set nice level
1372 if (jail->nice) {
1373 DEBUG(jail->pakfire, "Setting nice level to %d\n", jail->nice);
1374
1375 r = setpriority(PRIO_PROCESS, pid, jail->nice);
1376 if (r) {
1377 ERROR(jail->pakfire, "Could not set nice level: %m\n");
1378 return 1;
1379 }
1380 }
1381
1382 // Close other end of log pipes
1383 close(ctx->pipes.log_INFO[0]);
1384 close(ctx->pipes.log_ERROR[0]);
1385#ifdef ENABLE_DEBUG
1386 close(ctx->pipes.log_DEBUG[0]);
1387#endif /* ENABLE_DEBUG */
1388
1389 // Connect standard input
1390 if (ctx->pipes.stdin[0]) {
1391 r = dup2(ctx->pipes.stdin[0], STDIN_FILENO);
1392 if (r < 0) {
1393 ERROR(jail->pakfire, "Could not connect fd %d to stdin: %m\n",
1394 ctx->pipes.stdin[0]);
1395
1396 return 1;
1397 }
1398 }
1399
1400 // Connect standard output and error
1401 if (ctx->pipes.stdout[1] && ctx->pipes.stderr[1]) {
1402 r = dup2(ctx->pipes.stdout[1], STDOUT_FILENO);
1403 if (r < 0) {
1404 ERROR(jail->pakfire, "Could not connect fd %d to stdout: %m\n",
1405 ctx->pipes.stdout[1]);
1406
1407 return 1;
1408 }
1409
1410 r = dup2(ctx->pipes.stderr[1], STDERR_FILENO);
1411 if (r < 0) {
1412 ERROR(jail->pakfire, "Could not connect fd %d to stderr: %m\n",
1413 ctx->pipes.stderr[1]);
1414
1415 return 1;
1416 }
1417
1418 // Close the pipe (as we have moved the original file descriptors)
1419 pakfire_jail_close_pipe(jail, ctx->pipes.stdin);
1420 pakfire_jail_close_pipe(jail, ctx->pipes.stdout);
1421 pakfire_jail_close_pipe(jail, ctx->pipes.stderr);
1422 }
1423
1424 // Reset open file limit (http://0pointer.net/blog/file-descriptor-limits.html)
1425 r = pakfire_rlimit_reset_nofile(jail->pakfire);
1426 if (r)
1427 return r;
1428
1429 // Drop capabilities
1430 r = pakfire_jail_drop_capabilities(jail);
1431 if (r)
1432 return r;
1433
1434 // Filter syscalls
1435 r = pakfire_jail_limit_syscalls(jail);
1436 if (r)
1437 return r;
1438
1439 DEBUG(jail->pakfire, "Child process initialization done\n");
1440 DEBUG(jail->pakfire, "Launching command:\n");
1441
1442 // Log argv
1443 for (unsigned int i = 0; argv[i]; i++)
1444 DEBUG(jail->pakfire, " argv[%d] = %s\n", i, argv[i]);
1445
1446 // exec() command
1447 r = execvpe(argv[0], (char**)argv, jail->env);
1448 if (r < 0)
1449 ERROR(jail->pakfire, "Could not execve(): %m\n");
1450
1451 // Translate errno into regular exit code
1452 switch (errno) {
1453 case ENOENT:
1454 r = 127;
1455 break;
1456
1457 default:
1458 r = 1;
1459 }
1460
1461 // We should not get here
1462 return r;
1463}
1464
1465// Run a command in the jail
1466static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[],
1467 const int interactive,
1468 pakfire_jail_communicate_in communicate_in,
1469 pakfire_jail_communicate_out communicate_out,
1470 void* data) {
1471 int exit = -1;
1472 int r;
1473
1474 // Check if argv is valid
1475 if (!argv || !argv[0]) {
1476 errno = EINVAL;
1477 return -1;
1478 }
1479
1480 // Send any output to the default logger if no callback is set
1481 if (!communicate_out)
1482 communicate_out = pakfire_jail_default_log_callback;
1483
1484 // Initialize context for this call
1485 struct pakfire_jail_exec ctx = {
1486 .flags = 0,
1487
1488 .pipes = {
1489 .stdin = { 0, 0 },
1490 .stdout = { 0, 0 },
1491 .stderr = { 0, 0 },
1492 },
1493
1494 .communicate = {
1495 .in = communicate_in,
1496 .out = communicate_out,
1497 .data = data,
1498 },
1499 };
1500
1501 DEBUG(jail->pakfire, "Executing jail...\n");
1502
1503 // Enable networking in interactive mode
1504 if (interactive)
1505 ctx.flags |= PAKFIRE_JAIL_HAS_NETWORKING;
1506
1507 /*
1508 Setup a file descriptor which can be used to notify the client that the parent
1509 has completed configuration.
1510 */
1511 ctx.completed_fd = eventfd(0, EFD_CLOEXEC);
1512 if (ctx.completed_fd < 0) {
1513 ERROR(jail->pakfire, "eventfd() failed: %m\n");
1514 return -1;
1515 }
1516
1517 // Create pipes to communicate with child process if we are not running interactively
1518 if (!interactive) {
1519 // stdin (only if callback is set)
1520 if (ctx.communicate.in) {
1521 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdin, 0);
1522 if (r)
1523 goto ERROR;
1524 }
1525
1526 // stdout
1527 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdout, 0);
1528 if (r)
1529 goto ERROR;
1530
1531 // stderr
1532 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stderr, 0);
1533 if (r)
1534 goto ERROR;
1535 }
1536
1537 // Setup pipes for logging
1538 // INFO
1539 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_INFO, O_CLOEXEC);
1540 if (r)
1541 goto ERROR;
1542
1543 // ERROR
1544 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_ERROR, O_CLOEXEC);
1545 if (r)
1546 goto ERROR;
1547
1548#ifdef ENABLE_DEBUG
1549 // DEBUG
1550 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_DEBUG, O_CLOEXEC);
1551 if (r)
1552 goto ERROR;
1553#endif /* ENABLE_DEBUG */
1554
1555 // Configure child process
1556 struct clone_args args = {
1557 .flags =
1558 CLONE_NEWCGROUP |
1559 CLONE_NEWIPC |
1560 CLONE_NEWNS |
1561 CLONE_NEWPID |
1562 CLONE_NEWUSER |
1563 CLONE_NEWUTS |
1564 CLONE_PIDFD,
1565 .exit_signal = SIGCHLD,
1566 .pidfd = (long long unsigned int)&ctx.pidfd,
1567 };
1568
1569 // Launch the process in a cgroup that is a leaf of the configured cgroup
1570 if (jail->cgroup) {
1571 args.flags |= CLONE_INTO_CGROUP;
1572
1573 // Fetch our UUID
1574 const char* uuid = pakfire_jail_uuid(jail);
1575
1576 // Create a temporary cgroup
1577 r = pakfire_cgroup_child(&ctx.cgroup, jail->cgroup, uuid, 0);
1578 if (r) {
1579 ERROR(jail->pakfire, "Could not create cgroup for jail: %m\n");
1580 goto ERROR;
1581 }
1582
1583 // Clone into this cgroup
1584 args.cgroup = pakfire_cgroup_fd(ctx.cgroup);
1585 }
1586
1587 // Setup networking
1588 if (!pakfire_jail_exec_has_flag(&ctx, PAKFIRE_JAIL_HAS_NETWORKING)) {
1589 args.flags |= CLONE_NEWNET;
1590 }
1591
1592 // Fork this process
1593 ctx.pid = clone3(&args, sizeof(args));
1594 if (ctx.pid < 0) {
1595 ERROR(jail->pakfire, "Could not clone: %m\n");
1596 return -1;
1597
1598 // Child process
1599 } else if (ctx.pid == 0) {
1600 r = pakfire_jail_child(jail, &ctx, argv);
1601 _exit(r);
1602 }
1603
1604 // Parent process
1605 r = pakfire_jail_parent(jail, &ctx);
1606 if (r)
1607 goto ERROR;
1608
1609 DEBUG(jail->pakfire, "Waiting for PID %d to finish its work\n", ctx.pid);
1610
1611 // Read output of the child process
1612 r = pakfire_jail_wait(jail, &ctx);
1613 if (r)
1614 goto ERROR;
1615
1616 // Handle exit status
1617 switch (ctx.status.si_code) {
1618 case CLD_EXITED:
1619 DEBUG(jail->pakfire, "The child process exited with code %d\n",
1620 ctx.status.si_status);
1621
1622 // Pass exit code
1623 exit = ctx.status.si_status;
1624 break;
1625
1626 case CLD_KILLED:
1627 ERROR(jail->pakfire, "The child process was killed\n");
1628 exit = 139;
1629 break;
1630
1631 case CLD_DUMPED:
1632 ERROR(jail->pakfire, "The child process terminated abnormally\n");
1633 break;
1634
1635 // Log anything else
1636 default:
1637 ERROR(jail->pakfire, "Unknown child exit code: %d\n", ctx.status.si_code);
1638 break;
1639 }
1640
1641ERROR:
1642 // Destroy the temporary cgroup (if any)
1643 if (ctx.cgroup) {
1644 // Read cgroup stats
1645 r = pakfire_cgroup_stat(ctx.cgroup, &ctx.cgroup_stats);
1646 if (r) {
1647 ERROR(jail->pakfire, "Could not read cgroup stats: %m\n");
1648 } else {
1649 pakfire_cgroup_stat_dump(ctx.cgroup, &ctx.cgroup_stats);
1650 }
1651
1652 pakfire_cgroup_destroy(ctx.cgroup);
1653 pakfire_cgroup_unref(ctx.cgroup);
1654 }
1655
1656 // Close any file descriptors
1657 pakfire_jail_close_pipe(jail, ctx.pipes.stdin);
1658 pakfire_jail_close_pipe(jail, ctx.pipes.stdout);
1659 pakfire_jail_close_pipe(jail, ctx.pipes.stderr);
1660 if (ctx.pidfd)
1661 close(ctx.pidfd);
1662 pakfire_jail_close_pipe(jail, ctx.pipes.log_INFO);
1663 pakfire_jail_close_pipe(jail, ctx.pipes.log_ERROR);
1664 pakfire_jail_close_pipe(jail, ctx.pipes.log_DEBUG);
1665
1666 return exit;
1667}
1668
1669PAKFIRE_EXPORT int pakfire_jail_exec(
1670 struct pakfire_jail* jail,
1671 const char* argv[],
1672 pakfire_jail_communicate_in callback_in,
1673 pakfire_jail_communicate_out callback_out,
1674 void* data) {
1675 return __pakfire_jail_exec(jail, argv, 0, callback_in, callback_out, data);
1676}
1677
1678static int pakfire_jail_exec_interactive(
1679 struct pakfire_jail* jail, const char* argv[]) {
1680 int r;
1681
1682 // Setup interactive stuff
1683 r = pakfire_jail_setup_interactive_env(jail);
1684 if (r)
1685 return r;
1686
1687 return __pakfire_jail_exec(jail, argv, 1, NULL, NULL, NULL);
1688}
1689
1690int pakfire_jail_exec_script(struct pakfire_jail* jail,
1691 const char* script,
1692 const size_t size,
1693 const char* args[],
1694 pakfire_jail_communicate_in callback_in,
1695 pakfire_jail_communicate_out callback_out,
1696 void* data) {
1697 char path[PATH_MAX];
1698 const char** argv = NULL;
1699 FILE* f = NULL;
1700 int r;
1701
1702 const char* root = pakfire_get_path(jail->pakfire);
1703
1704 // Write the scriptlet to disk
1705 r = pakfire_path_join(path, root, PAKFIRE_TMP_DIR "/pakfire-script.XXXXXX");
1706 if (r)
1707 goto ERROR;
1708
1709 // Create a temporary file
1710 f = pakfire_mktemp(path, 0700);
1711 if (!f) {
1712 ERROR(jail->pakfire, "Could not create temporary file: %m\n");
1713 goto ERROR;
1714 }
1715
1716 DEBUG(jail->pakfire, "Writing script to %s:\n%.*s\n", path, (int)size, script);
1717
1718 // Write data
1719 r = fprintf(f, "%s", script);
1720 if (r < 0) {
1721 ERROR(jail->pakfire, "Could not write script to file %s: %m\n", path);
1722 goto ERROR;
1723 }
1724
1725 // Close file
1726 r = fclose(f);
1727 if (r) {
1728 ERROR(jail->pakfire, "Could not close script file %s: %m\n", path);
1729 goto ERROR;
1730 }
1731
1732 f = NULL;
1733
1734 // Count how many arguments were passed
1735 unsigned int argc = 1;
1736 if (args) {
1737 for (const char** arg = args; *arg; arg++)
1738 argc++;
1739 }
1740
1741 argv = calloc(argc + 1, sizeof(*argv));
1742 if (!argv) {
1743 ERROR(jail->pakfire, "Could not allocate argv: %m\n");
1744 goto ERROR;
1745 }
1746
1747 // Set command
1748 argv[0] = (root) ? pakfire_path_relpath(root, path) : path;
1749
1750 // Copy args
1751 for (unsigned int i = 1; i < argc; i++)
1752 argv[i] = args[i-1];
1753
1754 // Run the script
1755 r = pakfire_jail_exec(jail, argv, callback_in, callback_out, data);
1756
1757ERROR:
1758 if (argv)
1759 free(argv);
1760 if (f)
1761 fclose(f);
1762
1763 // Remove script from disk
1764 if (*path)
1765 unlink(path);
1766
1767 return r;
1768}
1769
1770/*
1771 A convenience function that creates a new jail, runs the given command and destroys
1772 the jail again.
1773*/
1774int pakfire_jail_run(struct pakfire* pakfire, const char* argv[], int flags, char** output) {
1775 struct pakfire_jail* jail = NULL;
1776 int r;
1777
1778 // Create a new jail
1779 r = pakfire_jail_create(&jail, pakfire, flags);
1780 if (r)
1781 goto ERROR;
1782
1783 // Execute the command
1784 r = pakfire_jail_exec(jail, argv, NULL, pakfire_jail_capture_stdout, output);
1785
1786ERROR:
1787 if (jail)
1788 pakfire_jail_unref(jail);
1789
1790 return r;
1791}
1792
1793int pakfire_jail_run_script(struct pakfire* pakfire,
1794 const char* script, const size_t length, const char* argv[], int flags) {
1795 struct pakfire_jail* jail = NULL;
1796 int r;
1797
1798 // Create a new jail
1799 r = pakfire_jail_create(&jail, pakfire, flags);
1800 if (r)
1801 goto ERROR;
1802
1803 // Execute the command
1804 r = pakfire_jail_exec_script(jail, script, length, argv, NULL, NULL, NULL);
1805
1806ERROR:
1807 if (jail)
1808 pakfire_jail_unref(jail);
1809
1810 return r;
1811}
1812
1813int pakfire_jail_shell(struct pakfire_jail* jail) {
1814 const char* argv[] = {
1815 "/bin/bash", "--login", NULL,
1816 };
1817
1818 // Execute /bin/bash
1819 return pakfire_jail_exec_interactive(jail, argv);
1820}
1821
1822int pakfire_jail_ldconfig(struct pakfire* pakfire) {
1823 char path[PATH_MAX];
1824
1825 const char* ldconfig = "/sbin/ldconfig";
1826
1827 // Check if ldconfig exists before calling it to avoid overhead
1828 int r = pakfire_path(pakfire, path, "%s", ldconfig);
1829 if (r)
1830 return r;
1831
1832 // Check if ldconfig is executable
1833 r = access(path, X_OK);
1834 if (r) {
1835 DEBUG(pakfire, "%s is not executable. Skipping...\n", ldconfig);
1836 return 0;
1837 }
1838
1839 const char* argv[] = {
1840 ldconfig, NULL,
1841 };
1842
1843 // Run ldconfig
1844 return pakfire_jail_run(pakfire, argv, 0, NULL);
1845}