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