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