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