]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/jail.c
jail: Set SYSTEMD_OFFLINE when in chroot
[pakfire.git] / src / libpakfire / jail.c
CommitLineData
fd37ccaf
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2022 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
32d5f21d 21#include <errno.h>
bcf09bf5 22#include <fcntl.h>
980b15af 23#include <linux/capability.h>
0bd84dc1 24#include <linux/sched.h>
58ee649f 25#include <sys/wait.h>
4f23b498 26#include <linux/wait.h>
0bd84dc1
MT
27#include <sched.h>
28#include <signal.h>
32d5f21d 29#include <stdlib.h>
0bd84dc1 30#include <syscall.h>
980b15af 31#include <sys/capability.h>
616f1fca 32#include <sys/epoll.h>
43dc0e16 33#include <sys/eventfd.h>
7bdf1d8e 34#include <sys/mount.h>
90d92b5c 35#include <sys/personality.h>
980b15af 36#include <sys/prctl.h>
cf440db8 37#include <sys/resource.h>
0bd84dc1
MT
38#include <sys/types.h>
39#include <sys/wait.h>
32d5f21d 40
739d5b57
MT
41// libseccomp
42#include <seccomp.h>
43
ae5201c5
MT
44// libuuid
45#include <uuid.h>
46
90d92b5c 47#include <pakfire/arch.h>
e3ddb498 48#include <pakfire/cgroup.h>
fd37ccaf 49#include <pakfire/jail.h>
4f59c39b
MT
50#include <pakfire/logging.h>
51#include <pakfire/mount.h>
fd37ccaf 52#include <pakfire/pakfire.h>
6ce56f90 53#include <pakfire/private.h>
4896e62c 54#include <pakfire/pwd.h>
d973a13d 55#include <pakfire/string.h>
32d5f21d
MT
56#include <pakfire/util.h>
57
616f1fca
MT
58#define BUFFER_SIZE 1024 * 64
59#define ENVIRON_SIZE 128
60#define EPOLL_MAX_EVENTS 2
cc6e2264 61#define MAX_MOUNTPOINTS 8
fd37ccaf 62
d5bc8fe0
MT
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[] = {
fbe95cdf 68 { "HOME", "/root" },
55f54757 69 { "LANG", "C.utf-8" },
a7ad6d4f 70 { "PATH", "/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin", },
d5bc8fe0
MT
71 { "TERM", "vt100" },
72 { NULL, NULL },
73};
74
cc6e2264
MT
75struct pakfire_jail_mountpoint {
76 char source[PATH_MAX];
77 char target[PATH_MAX];
78 int flags;
79};
80
fd37ccaf
MT
81struct pakfire_jail {
82 struct pakfire* pakfire;
83 int nrefs;
32d5f21d 84
ae5201c5
MT
85 // A unique ID for each jail
86 uuid_t uuid;
87 char __uuid[UUID_STR_LEN];
88
d639929b
MT
89 // Flags
90 int flags;
91
cf440db8
MT
92 // Resource Limits
93 int nice;
94
15503538
MT
95 // CGroup
96 struct pakfire_cgroup* cgroup;
97
32d5f21d
MT
98 // Environment
99 char* env[ENVIRON_SIZE];
616f1fca 100
cc6e2264
MT
101 // Mountpoints
102 struct pakfire_jail_mountpoint mountpoints[MAX_MOUNTPOINTS];
103 unsigned int num_mountpoints;
616f1fca
MT
104};
105
106struct pakfire_log_buffer {
107 char data[BUFFER_SIZE];
108 size_t used;
109};
110
7bdf1d8e
MT
111enum pakfire_jail_exec_flags {
112 PAKFIRE_JAIL_HAS_NETWORKING = (1 << 0),
113};
114
616f1fca 115struct pakfire_jail_exec {
7bdf1d8e
MT
116 int flags;
117
616f1fca
MT
118 // PID (of the child)
119 pid_t pid;
d853213d 120 int pidfd;
616f1fca 121
d853213d
MT
122 // Process status (from waitid)
123 siginfo_t status;
616f1fca 124
f7d240a7
MT
125 // FD to notify the client that the parent has finished initialization
126 int completed_fd;
127
616f1fca 128 // Log pipes
e33387d3 129 struct pakfire_jail_pipes {
2015cb92 130 int stdin[2];
616f1fca
MT
131 int stdout[2];
132 int stderr[2];
e33387d3
MT
133
134 // Logging
135 int log_INFO[2];
136 int log_ERROR[2];
137 int log_DEBUG[2];
616f1fca
MT
138 } pipes;
139
2015cb92
MT
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
616f1fca 147 // Log buffers
e33387d3 148 struct pakfire_jail_buffers {
616f1fca
MT
149 struct pakfire_log_buffer stdout;
150 struct pakfire_log_buffer stderr;
e33387d3
MT
151
152 // Logging
153 struct pakfire_log_buffer log_INFO;
154 struct pakfire_log_buffer log_ERROR;
155 struct pakfire_log_buffer log_DEBUG;
616f1fca 156 } buffers;
aca565fc
MT
157
158 struct pakfire_cgroup* cgroup;
6b7cf275 159 struct pakfire_cgroup_stats cgroup_stats;
fd37ccaf
MT
160};
161
0bd84dc1
MT
162static int clone3(struct clone_args* args, size_t size) {
163 return syscall(__NR_clone3, args, size);
164}
165
7bdf1d8e
MT
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
d5bc8fe0
MT
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
d34b1e00
MT
178 if (jail->cgroup)
179 pakfire_cgroup_unref(jail->cgroup);
180
d5bc8fe0
MT
181 pakfire_unref(jail->pakfire);
182 free(jail);
183}
184
e33387d3
MT
185/*
186 Passes any log messages on to the default pakfire log callback
187*/
fed41508
MT
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;
e33387d3
MT
198
199#ifdef ENABLE_DEBUG
200 case LOG_DEBUG:
201 DEBUG(pakfire, "%s", line);
202 break;
203#endif
fed41508
MT
204 }
205
206 return 0;
207}
208
00ba1d9a
MT
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
6ce56f90
MT
234PAKFIRE_EXPORT int pakfire_jail_create(struct pakfire_jail** jail,
235 struct pakfire* pakfire, int flags) {
d5bc8fe0
MT
236 int r;
237
238 // Allocate a new jail
fd37ccaf
MT
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
d639929b
MT
249 // Store flags
250 j->flags = flags;
251
ae5201c5
MT
252 // Generate a random UUID
253 uuid_generate_random(j->uuid);
254
84bd7655
MT
255 DEBUG(j->pakfire, "Allocated new jail at %p\n", j);
256
d5bc8fe0
MT
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
367e708d
MT
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
fd37ccaf
MT
271 // Done
272 *jail = j;
273 return 0;
84bd7655 274
d5bc8fe0
MT
275ERROR:
276 pakfire_jail_free(j);
32d5f21d 277
d5bc8fe0 278 return r;
fd37ccaf
MT
279}
280
6ce56f90 281PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_ref(struct pakfire_jail* jail) {
fd37ccaf
MT
282 ++jail->nrefs;
283
284 return jail;
285}
286
6ce56f90 287PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_unref(struct pakfire_jail* jail) {
fd37ccaf
MT
288 if (--jail->nrefs > 0)
289 return jail;
290
291 pakfire_jail_free(jail);
292 return NULL;
293}
32d5f21d 294
ae5201c5 295static const char* pakfire_jail_uuid(struct pakfire_jail* jail) {
8b2cc8ae 296 if (!*jail->__uuid)
ae5201c5
MT
297 uuid_unparse_lower(jail->uuid, jail->__uuid);
298
299 return jail->__uuid;
300}
301
cf440db8
MT
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
15503538
MT
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
32d5f21d
MT
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
6ce56f90
MT
368PAKFIRE_EXPORT const char* pakfire_jail_get_env(struct pakfire_jail* jail,
369 const char* key) {
32d5f21d
MT
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
6ce56f90
MT
378PAKFIRE_EXPORT int pakfire_jail_set_env(struct pakfire_jail* jail,
379 const char* key, const char* value) {
32d5f21d
MT
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}
9f50bf71 402
939025e7 403// Imports an environment
6ce56f90 404PAKFIRE_EXPORT int pakfire_jail_import_env(struct pakfire_jail* jail, const char* env[]) {
939025e7
MT
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
e33387d3
MT
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
616f1fca
MT
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,
e33387d3 479 struct pakfire_jail_exec* ctx, int priority, int fd,
2015cb92 480 struct pakfire_log_buffer* buffer, pakfire_jail_communicate_out callback, void* data) {
616f1fca
MT
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
e33387d3
MT
527 if (callback) {
528 int r = callback(jail->pakfire, data, priority, line, length);
616f1fca
MT
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
06b864ae
MT
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
f5a70a96
MT
553 // Skip if the writing pipe has already been closed
554 if (!ctx->pipes.stdin[1])
555 return 0;
556
06b864ae
MT
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
f5a70a96 568 // Close the file-descriptor
06b864ae 569 close(fd);
f5a70a96
MT
570
571 // Reset the file-descriptor so it won't be closed again later
572 ctx->pipes.stdin[1] = 0;
573
574 // Report success
06b864ae
MT
575 r = 0;
576 }
577
578 return r;
579}
580
195fe455
MT
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
e33387d3
MT
597/*
598 This is a convenience function to fetch the reading end of a pipe and
599 closes the write end.
600*/
06b864ae 601static int pakfire_jail_get_pipe_to_read(struct pakfire_jail* jail, int (*fds)[2]) {
e33387d3
MT
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
06b864ae
MT
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
d853213d 631static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
616f1fca
MT
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
06b864ae
MT
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);
d853213d 641 const int pidfd = ctx->pidfd;
616f1fca 642
e33387d3 643 // Logging
06b864ae
MT
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);
7ebfb7cb
MT
647
648 // Make a list of all file descriptors we are interested in
d853213d 649 int fds[] = {
06b864ae 650 stdin, stdout, stderr, pidfd, log_INFO, log_ERROR, log_DEBUG,
616f1fca
MT
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;
d853213d 658 goto ERROR;
616f1fca
MT
659 }
660
616f1fca 661 // Turn file descriptors into non-blocking mode and add them to epoll()
3aad8c0d 662 for (unsigned int i = 0; i < sizeof(fds) / sizeof(*fds); i++) {
616f1fca
MT
663 int fd = fds[i];
664
d853213d
MT
665 // Skip fds which were not initialized
666 if (fd <= 0)
667 continue;
668
e48beb24
MT
669 ev.events = EPOLLHUP;
670
671 if (fd == stdin)
672 ev.events |= EPOLLOUT;
673 else
674 ev.events |= EPOLLIN;
675
06b864ae
MT
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
616f1fca
MT
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;
d853213d 692 goto ERROR;
616f1fca
MT
693 }
694 }
695
696 int ended = 0;
697
698 // Loop for as long as the process is alive
699 while (!ended) {
616f1fca
MT
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
d853213d 709 goto ERROR;
616f1fca
MT
710 }
711
616f1fca 712 for (int i = 0; i < num; i++) {
e068b964 713 int e = events[i].events;
616f1fca
MT
714 int fd = events[i].data.fd;
715
e33387d3 716 struct pakfire_log_buffer* buffer = NULL;
2015cb92 717 pakfire_jail_communicate_out callback = NULL;
e33387d3
MT
718 void* data = NULL;
719 int priority;
720
e068b964
MT
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 }
d853213d 731
e068b964
MT
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;
d853213d 736
e068b964
MT
737 // Handle logging messages
738 } else if (fd == log_INFO) {
739 buffer = &ctx->buffers.log_INFO;
740 priority = LOG_INFO;
e33387d3 741
e068b964 742 callback = pakfire_jail_default_log_callback;
e33387d3 743
e068b964
MT
744 } else if (fd == log_ERROR) {
745 buffer = &ctx->buffers.log_ERROR;
746 priority = LOG_ERR;
e33387d3 747
e068b964 748 callback = pakfire_jail_default_log_callback;
e33387d3 749
e068b964
MT
750 } else if (fd == log_DEBUG) {
751 buffer = &ctx->buffers.log_DEBUG;
752 priority = LOG_DEBUG;
e33387d3 753
e068b964 754 callback = pakfire_jail_default_log_callback;
e33387d3 755
e068b964
MT
756 // Handle anything from the log pipes
757 } else if (fd == stdout) {
758 buffer = &ctx->buffers.stdout;
759 priority = LOG_INFO;
616f1fca 760
2015cb92
MT
761 callback = ctx->communicate.out;
762 data = ctx->communicate.data;
e33387d3 763
e068b964
MT
764 } else if (fd == stderr) {
765 buffer = &ctx->buffers.stderr;
766 priority = LOG_ERR;
616f1fca 767
2015cb92
MT
768 callback = ctx->communicate.out;
769 data = ctx->communicate.data;
e33387d3 770
e068b964
MT
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;
616f1fca
MT
780 }
781
06b864ae
MT
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
e068b964
MT
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 }
616f1fca
MT
809 }
810 }
811
d853213d 812ERROR:
616f1fca
MT
813 if (epollfd > 0)
814 close(epollfd);
815
816 return r;
817}
818
ccdd2e95
MT
819int pakfire_jail_capture_stdout(struct pakfire* pakfire, void* data,
820 int priority, const char* line, size_t length) {
12b9b39f
MT
821 char** output = (char**)data;
822 int r;
0de6bb30 823
2015cb92
MT
824 // Append everything from stdout to a buffer
825 if (output && priority == LOG_INFO) {
12b9b39f
MT
826 r = asprintf(output, "%s%s", (output && *output) ? *output : "", line);
827 if (r < 0)
0de6bb30 828 return 1;
0de6bb30
MT
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
980b15af
MT
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
739d5b57
MT
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
cc6e2264
MT
1003// Mountpoints
1004
061223f7 1005PAKFIRE_EXPORT int pakfire_jail_bind(struct pakfire_jail* jail,
cc6e2264
MT
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);
a60955af 1027 if (r) {
cc6e2264 1028 ERROR(jail->pakfire, "Could not copy source: %m\n");
a60955af 1029 return r;
cc6e2264
MT
1030 }
1031
1032 // Copy target
1033 r = pakfire_string_set(mp->target, target);
a60955af 1034 if (r) {
cc6e2264 1035 ERROR(jail->pakfire, "Could not copy target: %m\n");
a60955af 1036 return r;
cc6e2264
MT
1037 }
1038
1039 // Copy flags
1040 mp->flags = flags;
1041
1042 // Increment counter
1043 jail->num_mountpoints++;
1044
1045 return 0;
1046}
1047
7bdf1d8e
MT
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
cc6e2264
MT
1067/*
1068 Mounts everything that we require in the new namespace
1069*/
7bdf1d8e 1070static int pakfire_jail_mount(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
cc6e2264
MT
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
7bdf1d8e
MT
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
cc6e2264
MT
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
679ee2fa
MT
1103// UID/GID Mapping
1104
679ee2fa
MT
1105static int pakfire_jail_setup_uid_mapping(struct pakfire_jail* jail, pid_t pid) {
1106 char path[PATH_MAX];
1107 int r;
1108
4896e62c
MT
1109 // Skip mapping anything when running on /
1110 if (pakfire_on_root(jail->pakfire))
1111 return 0;
0f7f068b 1112
abe4ee37
MT
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
4896e62c 1121 // Fetch SUBUID
a1ff2863 1122 const struct pakfire_subid* subuid = pakfire_subuid(jail->pakfire);
4896e62c
MT
1123 if (!subuid)
1124 return 1;
679ee2fa 1125
abe4ee37 1126 /* When running as root, we will map the entire range.
679ee2fa 1127
abe4ee37
MT
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 */
679ee2fa 1131
abe4ee37
MT
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,
b64888fa 1138 "0 %lu 1\n1 %lu %lu\n", uid, subuid->id, subuid->length);
abe4ee37
MT
1139 }
1140
1141 if (r) {
1142 ERROR(jail->pakfire, "Could not map UIDs: %m\n");
1143 return r;
1144 }
1145
1146 return r;
679ee2fa
MT
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
4896e62c
MT
1153 // Skip mapping anything when running on /
1154 if (pakfire_on_root(jail->pakfire))
1155 return 0;
0f7f068b 1156
abe4ee37
MT
1157 // Fetch GID
1158 const gid_t gid = pakfire_gid(jail->pakfire);
1159
4896e62c 1160 // Fetch SUBGID
a1ff2863 1161 const struct pakfire_subid* subgid = pakfire_subgid(jail->pakfire);
4896e62c
MT
1162 if (!subgid)
1163 return 1;
679ee2fa
MT
1164
1165 // Make path
1166 r = pakfire_string_format(path, "/proc/%d/gid_map", pid);
a60955af
MT
1167 if (r)
1168 return r;
679ee2fa 1169
abe4ee37
MT
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 }
679ee2fa 1178
abe4ee37
MT
1179 if (r) {
1180 ERROR(jail->pakfire, "Could not map GIDs: %m\n");
1181 return r;
1182 }
1183
1184 return r;
679ee2fa
MT
1185}
1186
78d7488a
MT
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);
a60955af
MT
1193 if (r)
1194 return r;
78d7488a
MT
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
43dc0e16 1224static int pakfire_jail_send_signal(struct pakfire_jail* jail, int fd) {
743f449e
MT
1225 const uint64_t val = 1;
1226 int r = 0;
43dc0e16
MT
1227
1228 DEBUG(jail->pakfire, "Sending signal...\n");
1229
743f449e
MT
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
43dc0e16
MT
1238 close(fd);
1239
743f449e 1240 return r;
43dc0e16
MT
1241}
1242
1243static int pakfire_jail_wait_for_signal(struct pakfire_jail* jail, int fd) {
743f449e
MT
1244 uint64_t val = 0;
1245 int r = 0;
43dc0e16
MT
1246
1247 DEBUG(jail->pakfire, "Waiting for signal...\n");
1248
743f449e
MT
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
43dc0e16
MT
1256 close(fd);
1257
743f449e 1258 return r;
43dc0e16
MT
1259}
1260
679ee2fa
MT
1261/*
1262 Performs the initialisation that needs to happen in the parent part
1263*/
f7d240a7 1264static int pakfire_jail_parent(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
679ee2fa
MT
1265 int r;
1266
abe4ee37
MT
1267 // Setup UID mapping
1268 r = pakfire_jail_setup_uid_mapping(jail, ctx->pid);
679ee2fa
MT
1269 if (r)
1270 return r;
1271
abe4ee37
MT
1272 // Write "deny" to /proc/PID/setgroups
1273 r = pakfire_jail_setgroups(jail, ctx->pid);
78d7488a
MT
1274 if (r)
1275 return r;
1276
679ee2fa 1277 // Setup GID mapping
616f1fca 1278 r = pakfire_jail_setup_gid_mapping(jail, ctx->pid);
679ee2fa
MT
1279 if (r)
1280 return r;
1281
43dc0e16
MT
1282 // Parent has finished initialisation
1283 DEBUG(jail->pakfire, "Parent has finished initialization\n");
1284
1285 // Send signal to client
f7d240a7 1286 r = pakfire_jail_send_signal(jail, ctx->completed_fd);
43dc0e16
MT
1287 if (r)
1288 return r;
1289
679ee2fa
MT
1290 return 0;
1291}
1292
616f1fca 1293static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
f7d240a7 1294 const char* argv[]) {
43dc0e16
MT
1295 int r;
1296
e33387d3
MT
1297 // Redirect any logging to our log pipe
1298 pakfire_set_log_callback(jail->pakfire, pakfire_jail_log, &ctx->pipes);
0bd84dc1 1299
2a7b5e00
MT
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
cf440db8
MT
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);
0bd84dc1 1311
43dc0e16 1312 // Wait for the parent to finish initialization
f7d240a7 1313 r = pakfire_jail_wait_for_signal(jail, ctx->completed_fd);
43dc0e16
MT
1314 if (r)
1315 return r;
1316
4f59c39b
MT
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)
4f719e21 1331 if (uid || gid || euid || egid) {
4f59c39b
MT
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
7bdf1d8e 1342 r = pakfire_jail_mount(jail, ctx);
4f59c39b
MT
1343 if (r)
1344 return r;
1345
4f59c39b
MT
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
90d92b5c
MT
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
cf440db8
MT
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
e33387d3
MT
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
2015cb92
MT
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
7ebfb7cb
MT
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
195fe455 1418 // Close the pipe (as we have moved the original file descriptors)
2015cb92 1419 pakfire_jail_close_pipe(jail, ctx->pipes.stdin);
195fe455
MT
1420 pakfire_jail_close_pipe(jail, ctx->pipes.stdout);
1421 pakfire_jail_close_pipe(jail, ctx->pipes.stderr);
7ebfb7cb
MT
1422 }
1423
007bc66c
MT
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
980b15af
MT
1429 // Drop capabilities
1430 r = pakfire_jail_drop_capabilities(jail);
1431 if (r)
1432 return r;
1433
739d5b57
MT
1434 // Filter syscalls
1435 r = pakfire_jail_limit_syscalls(jail);
1436 if (r)
1437 return r;
1438
2015cb92
MT
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
b3498aeb
MT
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;
0bd84dc1
MT
1463}
1464
9f50bf71 1465// Run a command in the jail
db4f234f 1466static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[],
2015cb92
MT
1467 const int interactive,
1468 pakfire_jail_communicate_in communicate_in,
1469 pakfire_jail_communicate_out communicate_out,
1470 void* data) {
4f59c39b 1471 int exit = -1;
0bd84dc1
MT
1472 int r;
1473
b3498aeb
MT
1474 // Check if argv is valid
1475 if (!argv || !argv[0]) {
1476 errno = EINVAL;
1477 return -1;
1478 }
1479
2015cb92
MT
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
616f1fca
MT
1484 // Initialize context for this call
1485 struct pakfire_jail_exec ctx = {
7bdf1d8e
MT
1486 .flags = 0,
1487
616f1fca 1488 .pipes = {
2015cb92
MT
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,
616f1fca 1498 },
616f1fca
MT
1499 };
1500
0bd84dc1
MT
1501 DEBUG(jail->pakfire, "Executing jail...\n");
1502
7bdf1d8e
MT
1503 // Enable networking in interactive mode
1504 if (interactive)
1505 ctx.flags |= PAKFIRE_JAIL_HAS_NETWORKING;
1506
43dc0e16
MT
1507 /*
1508 Setup a file descriptor which can be used to notify the client that the parent
1509 has completed configuration.
1510 */
f7d240a7
MT
1511 ctx.completed_fd = eventfd(0, EFD_CLOEXEC);
1512 if (ctx.completed_fd < 0) {
43dc0e16
MT
1513 ERROR(jail->pakfire, "eventfd() failed: %m\n");
1514 return -1;
1515 }
1516
616f1fca 1517 // Create pipes to communicate with child process if we are not running interactively
58963c75 1518 if (!interactive) {
2015cb92
MT
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
616f1fca 1526 // stdout
e33387d3
MT
1527 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdout, 0);
1528 if (r)
616f1fca 1529 goto ERROR;
616f1fca
MT
1530
1531 // stderr
e33387d3
MT
1532 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stderr, 0);
1533 if (r)
616f1fca 1534 goto ERROR;
616f1fca
MT
1535 }
1536
e33387d3
MT
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
0bd84dc1
MT
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 |
d853213d 1563 CLONE_NEWUTS |
02fd4f8b 1564 CLONE_PIDFD,
0bd84dc1 1565 .exit_signal = SIGCHLD,
d853213d 1566 .pidfd = (long long unsigned int)&ctx.pidfd,
0bd84dc1
MT
1567 };
1568
aca565fc 1569 // Launch the process in a cgroup that is a leaf of the configured cgroup
02fd4f8b
MT
1570 if (jail->cgroup) {
1571 args.flags |= CLONE_INTO_CGROUP;
1572
ae5201c5
MT
1573 // Fetch our UUID
1574 const char* uuid = pakfire_jail_uuid(jail);
aca565fc
MT
1575
1576 // Create a temporary cgroup
ae5201c5 1577 r = pakfire_cgroup_child(&ctx.cgroup, jail->cgroup, uuid, 0);
aca565fc
MT
1578 if (r) {
1579 ERROR(jail->pakfire, "Could not create cgroup for jail: %m\n");
1580 goto ERROR;
1581 }
1582
02fd4f8b 1583 // Clone into this cgroup
aca565fc 1584 args.cgroup = pakfire_cgroup_fd(ctx.cgroup);
02fd4f8b
MT
1585 }
1586
7bdf1d8e
MT
1587 // Setup networking
1588 if (!pakfire_jail_exec_has_flag(&ctx, PAKFIRE_JAIL_HAS_NETWORKING)) {
1589 args.flags |= CLONE_NEWNET;
1590 }
1591
0bd84dc1 1592 // Fork this process
616f1fca
MT
1593 ctx.pid = clone3(&args, sizeof(args));
1594 if (ctx.pid < 0) {
0bd84dc1
MT
1595 ERROR(jail->pakfire, "Could not clone: %m\n");
1596 return -1;
1597
1598 // Child process
616f1fca 1599 } else if (ctx.pid == 0) {
f7d240a7 1600 r = pakfire_jail_child(jail, &ctx, argv);
0bd84dc1
MT
1601 _exit(r);
1602 }
1603
679ee2fa 1604 // Parent process
f7d240a7 1605 r = pakfire_jail_parent(jail, &ctx);
679ee2fa
MT
1606 if (r)
1607 goto ERROR;
1608
616f1fca 1609 DEBUG(jail->pakfire, "Waiting for PID %d to finish its work\n", ctx.pid);
0bd84dc1 1610
616f1fca 1611 // Read output of the child process
d853213d
MT
1612 r = pakfire_jail_wait(jail, &ctx);
1613 if (r)
1614 goto ERROR;
0bd84dc1 1615
d853213d
MT
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);
616f1fca 1621
d853213d
MT
1622 // Pass exit code
1623 exit = ctx.status.si_status;
1624 break;
0bd84dc1 1625
d853213d 1626 case CLD_KILLED:
d853213d 1627 ERROR(jail->pakfire, "The child process was killed\n");
54f64dc5
MT
1628 exit = 139;
1629 break;
1630
1631 case CLD_DUMPED:
1632 ERROR(jail->pakfire, "The child process terminated abnormally\n");
d853213d 1633 break;
0bd84dc1 1634
d853213d
MT
1635 // Log anything else
1636 default:
1637 ERROR(jail->pakfire, "Unknown child exit code: %d\n", ctx.status.si_code);
1638 break;
0bd84dc1
MT
1639 }
1640
679ee2fa 1641ERROR:
aca565fc
MT
1642 // Destroy the temporary cgroup (if any)
1643 if (ctx.cgroup) {
6b7cf275
MT
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
aca565fc
MT
1652 pakfire_cgroup_destroy(ctx.cgroup);
1653 pakfire_cgroup_unref(ctx.cgroup);
1654 }
1655
616f1fca 1656 // Close any file descriptors
2015cb92 1657 pakfire_jail_close_pipe(jail, ctx.pipes.stdin);
e33387d3
MT
1658 pakfire_jail_close_pipe(jail, ctx.pipes.stdout);
1659 pakfire_jail_close_pipe(jail, ctx.pipes.stderr);
d853213d
MT
1660 if (ctx.pidfd)
1661 close(ctx.pidfd);
e33387d3
MT
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);
616f1fca 1665
4f59c39b 1666 return exit;
9f50bf71 1667}
a45ed6b0 1668
ccdd2e95 1669PAKFIRE_EXPORT int pakfire_jail_exec(
2015cb92
MT
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
db4f234f
MT
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
2015cb92 1687 return __pakfire_jail_exec(jail, argv, 1, NULL, NULL, NULL);
db4f234f
MT
1688}
1689
ccdd2e95
MT
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) {
a45ed6b0
MT
1697 char path[PATH_MAX];
1698 const char** argv = NULL;
35291cb7 1699 FILE* f = NULL;
a45ed6b0
MT
1700 int r;
1701
1702 const char* root = pakfire_get_path(jail->pakfire);
1703
1704 // Write the scriptlet to disk
35291cb7 1705 r = pakfire_path_join(path, root, PAKFIRE_TMP_DIR "/pakfire-script.XXXXXX");
56796f84 1706 if (r)
a45ed6b0
MT
1707 goto ERROR;
1708
35291cb7
MT
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");
a45ed6b0
MT
1713 goto ERROR;
1714 }
1715
1716 DEBUG(jail->pakfire, "Writing script to %s:\n%.*s\n", path, (int)size, script);
1717
1718 // Write data
35291cb7
MT
1719 r = fprintf(f, "%s", script);
1720 if (r < 0) {
a45ed6b0 1721 ERROR(jail->pakfire, "Could not write script to file %s: %m\n", path);
a45ed6b0
MT
1722 goto ERROR;
1723 }
1724
1725 // Close file
35291cb7 1726 r = fclose(f);
a45ed6b0
MT
1727 if (r) {
1728 ERROR(jail->pakfire, "Could not close script file %s: %m\n", path);
a45ed6b0
MT
1729 goto ERROR;
1730 }
1731
35291cb7
MT
1732 f = NULL;
1733
a45ed6b0
MT
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
ccdd2e95 1755 r = pakfire_jail_exec(jail, argv, callback_in, callback_out, data);
a45ed6b0
MT
1756
1757ERROR:
1758 if (argv)
1759 free(argv);
35291cb7
MT
1760 if (f)
1761 fclose(f);
a45ed6b0
MT
1762
1763 // Remove script from disk
1764 if (*path)
1765 unlink(path);
1766
1767 return r;
1768}
82df3c77
MT
1769
1770/*
1771 A convenience function that creates a new jail, runs the given command and destroys
1772 the jail again.
1773*/
12b9b39f 1774int pakfire_jail_run(struct pakfire* pakfire, const char* argv[], int flags, char** output) {
82df3c77
MT
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
ccdd2e95 1784 r = pakfire_jail_exec(jail, argv, NULL, pakfire_jail_capture_stdout, output);
82df3c77
MT
1785
1786ERROR:
1787 if (jail)
1788 pakfire_jail_unref(jail);
1789
1790 return r;
1791}
4f688bd8
MT
1792
1793int pakfire_jail_run_script(struct pakfire* pakfire,
49a9babc 1794 const char* script, const size_t length, const char* argv[], int flags) {
4f688bd8
MT
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
49a9babc 1804 r = pakfire_jail_exec_script(jail, script, length, argv, NULL, NULL, NULL);
4f688bd8
MT
1805
1806ERROR:
1807 if (jail)
1808 pakfire_jail_unref(jail);
1809
1810 return r;
1811}
e43489f7 1812
5f6e42a2 1813int pakfire_jail_shell(struct pakfire_jail* jail) {
e43489f7
MT
1814 const char* argv[] = {
1815 "/bin/bash", "--login", NULL,
1816 };
1817
1818 // Execute /bin/bash
db4f234f 1819 return pakfire_jail_exec_interactive(jail, argv);
e43489f7
MT
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
77e26129
MT
1828 int r = pakfire_path(pakfire, path, "%s", ldconfig);
1829 if (r)
1830 return r;
e43489f7
MT
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}