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