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