/
githubmirror
/
libcap-ng
Обзор
Документация
Войти
/
githubmirror
/
libcap-ng
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
utils/cap-audit/cap_audit_util.c
344 строки
7 KB
Steve Grubb
Fix SPDX License tags
19 апр 2026, 19:16
19 апр 2026, 19:16
ea6a054
Код
Авторство
О чём код?
// SPDX-License-Identifier: GPL-2.0-or-later /* * cap-audit - Trace a target process to discover required capabilities. * Copyright (c) 2026 Red Hat Inc. * All Rights Reserved. * * This software may be freely redistributed and/or modified under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Authors: * Steve Grubb <sgrubb@redhat.com> * Portions of this code were made with codex 5.2 */ /* Shared utility helpers: target inspection, syscall/cap name lookup, * output escaping, and common capability aggregation functions. */ #include "cap_audit.h" #include <errno.h> #include <fcntl.h> #include <libaudit.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> int resolve_target_exe(pid_t pid, char *exepath, size_t exepath_len) { char linkpath[64]; char selfpath[PATH_MAX]; ssize_t len; ssize_t self_len; int tries = 50; if (snprintf(linkpath, sizeof(linkpath), "/proc/%d/exe", pid) < 0) return -1; self_len = readlink("/proc/self/exe", selfpath, sizeof(selfpath) - 1); if (self_len >= 0) selfpath[self_len] = '\0'; while (tries--) { len = readlink(linkpath, exepath, exepath_len - 1); if (len < 0) { fprintf(stderr, "Warning: readlink(%s) failed: %s\n", linkpath, strerror(errno)); return -1; } exepath[len] = '\0'; if (self_len < 0 || strcmp(exepath, selfpath) != 0) break; if (tries == 0) fprintf(stderr, "Warning: %s still points to auditor binary (%s)\n", linkpath, exepath); usleep(10000); } return 0; } int inspect_target_file_caps(pid_t pid) { char exepath[PATH_MAX]; int fd; struct stat st; capng_results_t caps; state.app.file_caps = 0; state.app.file_setpcap = 0; if (resolve_target_exe(pid, exepath, sizeof(exepath)) < 0) return -1; fd = open(exepath, O_RDONLY | O_CLOEXEC); if (fd < 0) { fprintf(stderr, "Warning: open(%s) failed: %s\n", exepath, strerror(errno)); return -1; } if (fstat(fd, &st) < 0) { fprintf(stderr, "Warning: fstat(%s) failed: %s\n", exepath, strerror(errno)); close(fd); return -1; } if (!S_ISREG(st.st_mode)) { fprintf(stderr, "Warning: %s is not a regular file\n", exepath); close(fd); return -1; } capng_clear(CAPNG_SELECT_BOTH); if (capng_get_caps_fd(fd)) { if (errno != ENODATA) fprintf(stderr, "Warning: capng_get_caps_fd(%s) failed: %s\n", exepath, strerror(errno)); close(fd); if (capng_get_caps_process()) fprintf(stderr, "Warning: failed to restore process capabilities\n"); return -1; } close(fd); caps = capng_have_capabilities(CAPNG_SELECT_CAPS); if (caps == CAPNG_NONE) caps = capng_have_permitted_capabilities(); if (caps > CAPNG_NONE) state.app.file_caps = 1; if (capng_have_capability(CAPNG_PERMITTED, CAP_SETPCAP) || capng_have_capability(CAPNG_INHERITABLE, CAP_SETPCAP)) state.app.file_setpcap = 1; if (state.verbose) printf("[*] File caps source: %s (has_caps=%d setpcap=%d)\n", exepath, state.app.file_caps, state.app.file_setpcap); if (capng_get_caps_process()) fprintf(stderr, "Warning: failed to restore process capabilities\n"); return 0; } const char *cap_name_safe(int cap) { const char *name = capng_capability_to_name(cap); return name ? name : "unknown"; } void read_sysctl(const char *path, int *value) { FILE *f; f = fopen(path, "r"); if (f) { if (fscanf(f, "%d", value) != 1) *value = -1; fclose(f); } else { *value = -1; } } void read_system_state(struct app_caps *app) { FILE *f; read_sysctl("/proc/sys/kernel/yama/ptrace_scope", &app->yama_ptrace_scope); read_sysctl("/proc/sys/kernel/kptr_restrict", &app->kptr_restrict); read_sysctl("/proc/sys/kernel/dmesg_restrict", &app->dmesg_restrict); read_sysctl("/proc/sys/kernel/modules_disabled", &app->modules_disabled); read_sysctl("/proc/sys/kernel/perf_event_paranoid", &app->perf_event_paranoid); read_sysctl("/proc/sys/kernel/unprivileged_bpf_disabled", &app->unprivileged_bpf_disabled); read_sysctl("/proc/sys/net/core/bpf_jit_enable", &app->bpf_jit_enable); read_sysctl("/proc/sys/net/core/bpf_jit_harden", &app->bpf_jit_harden); read_sysctl("/proc/sys/net/core/bpf_jit_kallsyms", &app->bpf_jit_kallsyms); read_sysctl("/proc/sys/vm/mmap_min_addr", &app->mmap_min_addr); read_sysctl("/proc/sys/fs/protected_hardlinks", &app->protected_hardlinks); read_sysctl("/proc/sys/fs/protected_symlinks", &app->protected_symlinks); read_sysctl("/proc/sys/fs/suid_dumpable", &app->suid_dumpable); f = fopen("/proc/sys/kernel/osrelease", "r"); if (f) { if (!fgets(app->kernel_version, sizeof(app->kernel_version), f)) app->kernel_version[0] = 0; app->kernel_version[strcspn(app->kernel_version, "\n")] = 0; fclose(f); } } const char *syscall_name_from_nr(int nr) { if (audit_machine < 0) return NULL; return audit_syscall_to_name(nr, audit_machine); } char *json_escape(const char *input) { size_t i; size_t needed = 0; char *out; char *pos; if (!input) return strdup(""); for (i = 0; input[i]; i++) { unsigned char c = input[i]; switch (c) { case '\"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': needed += 2; break; default: if (c < 0x20) needed += 6; else needed++; break; } } out = malloc(needed + 1); if (!out) return NULL; pos = out; for (i = 0; input[i]; i++) { unsigned char c = input[i]; switch (c) { case '\"': *pos++ = '\\'; *pos++ = '\"'; break; case '\\': *pos++ = '\\'; *pos++ = '\\'; break; case '\b': *pos++ = '\\'; *pos++ = 'b'; break; case '\f': *pos++ = '\\'; *pos++ = 'f'; break; case '\n': *pos++ = '\\'; *pos++ = 'n'; break; case '\r': *pos++ = '\\'; *pos++ = 'r'; break; case '\t': *pos++ = '\\'; *pos++ = 't'; break; default: if (c < 0x20) { snprintf(pos, 7, "\\u%04x", c); pos += 6; } else { *pos++ = c; } break; } } *pos = '\0'; return out; } void update_reason_to(char **target, int syscall_nr) { const char *syscall_name; if (*target) free(*target); if (syscall_nr < 0) { if (asprintf(target, "Used during capability check (syscall unknown)") < 0) *target = NULL; return; } syscall_name = syscall_name_from_nr(syscall_nr); if (asprintf(target, "Used by %s", syscall_name ? syscall_name : "unknown") < 0) *target = NULL; } void update_reason(struct cap_check *check, int syscall_nr) { update_reason_to(&check->reason, syscall_nr); } void update_reason_op(struct cap_check *check, int syscall_nr) { update_reason_to(&check->op_reason, syscall_nr); } int cap_required_union(const struct cap_check *check) { return check->granted > 0 || check->op_granted > 0; } unsigned long cap_total_checks(const struct cap_check *check) { return check->count + check->op_count; } unsigned long cap_total_granted(const struct cap_check *check) { return check->granted + check->op_granted; } unsigned long cap_total_denied(const struct cap_check *check) { return check->denied + check->op_denied; } const char *cap_union_reason(const struct cap_check *check) { if (check->reason) return check->reason; return check->op_reason; }