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