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