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