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