32 #include <sys/types.h>
42 #define ZP_COMPAUDIT_CACHE_SUBDIR "zpmod"
43 #define ZP_COMPAUDIT_CACHE_FILE "compaudit_v3.zcache"
50 const char *xdg =
getsparam(
"XDG_CACHE_HOME");
52 const char *base = (xdg && *xdg) ? xdg : NULL;
53 if (!base && home && *home) {
54 size_t len = strlen(home) +
sizeof(
"/.cache");
55 char *p = (
char *)
zalloc(len);
74 zwarnnam(nam,
"%scompaudit-cache: cannot resolve cache directory (no HOME)",
80 char *full = (
char *)
zalloc(len);
87 if (stat(full, &st) != 0) {
88 if (mkdir(full, 0700) != 0) {
90 full, strerror(errno));
96 if (!S_ISDIR(st.st_mode)) {
97 zwarnnam(nam,
"%scompaudit-cache: %s exists and is not a directory",
114 char *p = (
char *)
zalloc(len);
152 size_t ncap = v->
cap ? v->
cap * 2 : 8;
167 for (
size_t i = 0; i < v->
size; i++) {
197 const char *candidates[] = {
"/proc/self/exe", NULL};
198 for (
int cand_index = 0; candidates[cand_index]; ++cand_index) {
200 if (stat(candidates[cand_index], &st) == 0) {
208 if (uid == 0 || uid ==
self) {
221 int sticky = (st->st_mode & S_ISVTX) ? 1 : 0;
222 int world_w = (st->st_mode & S_IWOTH) ? 1 : 0;
223 int group_w = (st->st_mode & S_IWGRP) ? 1 : 0;
225 if (!allowed_owner) {
226 if ((world_w && !sticky) || (group_w && !sticky)) {
232 if (world_w && !sticky) {
245 for (
char **fpath_it = arr; *fpath_it; ++fpath_it) {
249 size_t len = strlen(*fpath_it) + 1;
250 char *dup = (
char *)
zalloc(len);
254 memcpy(dup, *fpath_it, len);
272 FILE *fp = fopen(
path,
"r");
277 if (!fgets(line,
sizeof(line), fp)) {
281 if (strncmp(line,
"version:3", 9) != 0) {
285 while (fgets(line,
sizeof(line), fp)) {
286 if (line[0] ==
'\n' || line[0] ==
'#') {
289 char *nl = strchr(line,
'\n');
294 char *tok = strtok_r(line,
"\t", &save);
298 char *path_dup =
ztrdup(tok);
302 while (fld < 8 && (tok = strtok_r(NULL,
"\t", &save))) {
303 int base = (fld == 1) ? 8 : 10;
304 fields[fld] = strtol(tok, NULL, base);
316 e.
mode = (mode_t)fields[1];
317 e.
uid = (uid_t)fields[2];
318 e.
gid = (gid_t)fields[3];
319 e.
mtime = (time_t)fields[4];
320 e.
ctime = (time_t)fields[5];
336 uid_t
self = geteuid();
337 for (
size_t i = 0; i < cache->
size; i++) {
339 if (stat(cache->
items[i].
path, &st) != 0) {
345 if (cache->
items[i].
mode != (st.st_mode & 07777)) {
368 int *out_insecure,
int *out_secure) {
369 uid_t
self = geteuid();
371 for (
size_t i = 0; i < entries->
size;
378 for (
char **fpath_it2 = arr; *fpath_it2; ++fpath_it2) {
383 for (
size_t ent_idx = 0; ent_idx < entries->
size; ent_idx++) {
384 if (strcmp(entries->
items[ent_idx].
path, *fpath_it2) == 0) {
390 size_t len = strlen(*fpath_it2) + 1;
391 char *dup = (
char *)
zalloc(len);
395 memcpy(dup, *fpath_it2, len);
415 FILE *fp = fopen(cache_path,
"w");
417 zwarnnam(nam,
"%scompaudit-cache: cannot update %s: %s",
zp_icon(
"❌ "),
418 cache_path, strerror(errno));
421 fchmod(fileno(fp), 0600);
422 fprintf(fp,
"version:3\n");
423 for (
size_t i = 0; i < entries->
size; i++) {
425 if (stat(entries->
items[i].
path, &st) != 0) {
430 if (!verdict && p_insec) {
434 entries->
items[i].
mode = (st.st_mode & 07777);
446 if (verdict && !zwc_insec) {
447 DIR *d = opendir(entries->
items[i].
path);
450 while ((de = readdir(d))) {
451 if (de->d_name[0] ==
'.') {
454 size_t nlen = strlen(de->d_name);
455 if (nlen >= 4 && strcmp(de->d_name + nlen - 4,
".zwc") == 0) {
457 if (snprintf(full,
sizeof(full),
"%s/%s", entries->
items[i].
path,
458 de->d_name) < (
int)
sizeof(full)) {
460 if (stat(full, &stf) == 0 && (stf.st_mode & 022)) {
471 fprintf(fp,
"%s\t%d\t%o\t%lu\t%lu\t%ld\t%ld\t%d\t%d\n",
472 entries->
items[i].
path, verdict, (
unsigned)(st.st_mode & 07777),
473 (
unsigned long)st.st_uid, (
unsigned long)st.st_gid,
474 (
long)st.st_mtime, (
long)st.st_ctime, p_insec, zwc_insec);
478 *out_insecure = insecure;
481 *out_secure = secure;
490 size_t len = strlen(
path);
491 if (len >=
sizeof(buf)) {
494 memcpy(buf,
path, len + 1);
496 char *slash = strrchr(buf,
'/');
497 if (!slash || slash == buf) {
502 if (stat(buf, &st) != 0) {
514 struct zp_cc_vec *targets,
int *out_insecure,
516 uid_t
self = geteuid();
519 FILE *fp = fopen(file_path,
"w");
522 file_path, strerror(errno));
525 fchmod(fileno(fp), 0600);
526 fprintf(fp,
"version:3\n");
527 for (
size_t i = 0; i < targets->
size; i++) {
529 if (stat(targets->
items[i].
path, &st) != 0) {
534 if (!verdict && p_insec) {
544 DIR *d = opendir(targets->
items[i].
path);
547 while ((de = readdir(d))) {
548 if (de->d_name[0] ==
'.') {
551 size_t nlen = strlen(de->d_name);
552 if (nlen >= 4 && strcmp(de->d_name + nlen - 4,
".zwc") == 0) {
554 if (snprintf(full,
sizeof(full),
"%s/%s", targets->
items[i].
path,
555 de->d_name) < (
int)
sizeof(full)) {
557 if (stat(full, &stf) == 0 && (stf.st_mode & 022)) {
567 fprintf(fp,
"%s\t%d\t%o\t%lu\t%lu\t%ld\t%ld\t%d\t%d\n",
568 targets->
items[i].
path, verdict, (
unsigned)(st.st_mode & 07777),
569 (
unsigned long)st.st_uid, (
unsigned long)st.st_gid,
570 (
long)st.st_mtime, (
long)st.st_ctime, p_insec, zwc_insec);
574 *out_insecure = insecure;
577 *out_secure = secure;
594 if (stat(cache_path, &st_new) != 0) {
595 const char *needle =
"_v3.zcache";
596 const char *rep =
"_v2.zcache";
597 char *p = strstr(cache_path, needle);
599 size_t prefix = (size_t)(p - cache_path);
600 size_t v2len = prefix + strlen(rep) + 1;
601 char *v2path = (
char *)
zalloc(v2len);
603 memcpy(v2path, cache_path, prefix);
604 strcpy(v2path + prefix, rep);
606 if (stat(v2path, &st_old) == 0) {
622 if (!rebuild && stat(cache_path, &cst) == 0) {
623 if ((cst.st_mode & S_IWOTH) || (cst.st_mode & S_IWGRP)) {
646 if (insecure || secure) {
653 if (
zp_cc_rebuild(nam, cache_path, &targets, &insecure, &secure) != 0) {
665 for (
size_t i = 0; i < cache_entries.
size; i++) {
674 fprintf(stdout,
"{\"insecure\":%d,\"secure\":%d,\"dirs\":[", insecure,
676 uid_t
self = geteuid();
677 for (
size_t i = 0; i < cache_entries.
size; i++) {
680 int reason_ancestor = 0;
685 if (stat(e->
path, &st_dir) == 0) {
697 fprintf(stdout,
"%s{\"path\":\"", i ?
"," :
"");
698 const char *p = e->
path;
700 if (*p ==
'\\' || *p ==
'\"') {
705 fprintf(stdout,
"\",\"verdict\":%d,\"parent_insecure\":%d,\"reasons\":[",
709 fprintf(stdout,
"\"dir_perms\"");
712 if (reason_ancestor) {
713 fprintf(stdout,
"%s\"ancestor_perms\"", first ?
"" :
" ,");
717 fprintf(stdout,
"%s\"zwc_perms\"", first ?
"" :
" ,");
719 fprintf(stdout,
"]}");
721 fprintf(stdout,
"]}\n");
724 fprintf(stdout,
"%scompaudit-cache: insecure %d secure %d\n",
725 zp_icon(
"🔐 "), insecure, secure);
726 if (cache_entries.
size) {
727 for (
size_t i = 0; i < cache_entries.
size; i++) {
729 fprintf(stdout,
" ! %s%s\n", cache_entries.
items[i].
path,
static void zp_cc_collect_dirs(struct zp_cc_vec *out)
static void zp_cc_vec_init(struct zp_cc_vec *v)
#define ZP_COMPAUDIT_CACHE_FILE
static void zp_cc_discover_exe_owner(void)
static int zp_cc_validate_cache(struct zp_cc_vec *cache)
static char * zp_cc_cache_file_path(char *nam)
int zp_compaudit_cache_core(char *nam, int rebuild, int show, int json)
static char * zp_cc_ensure_dir(char *nam)
static uid_t zp_cached_exe_owner
static int zp_cc_incremental_update(char *nam, char *cache_path, struct zp_cc_vec *entries, int *out_insecure, int *out_secure)
static char * zp_cc_base_cache_dir(void)
static int zp_cc_load_cache(const char *path, struct zp_cc_vec *out)
static int zp_cc_vec_push(struct zp_cc_vec *v, struct zp_cc_entry *e)
static void zp_cc_vec_free(struct zp_cc_vec *v)
static int zp_cc_rebuild(char *nam, const char *file_path, struct zp_cc_vec *targets, int *out_insecure, int *out_secure)
static int zp_cc_owner_allowed(uid_t uid, uid_t self)
static int zp_cc_is_insecure(struct stat *st, uid_t self)
static int parent_insecure_path(const char *path, uid_t self)
#define ZP_COMPAUDIT_CACHE_SUBDIR
const char * zp_icon(const char *s)
Return icon string if enabled, empty string otherwise.
struct zp_cc_entry * items
Module declaration header (mdh) for zpmod.
Prototype stub for zpmod when building out-of-tree.
Interface for cached compaudit security verdicts.
Optional terminal/locale detection for emoji support in messages.
void * zalloc(size_t size)
void zfree(void *ptr, size_t size)
char ** getaparam(const char *name)
void * zrealloc(void *ptr, size_t size)
char * getsparam(const char *name)
void zwarnnam(const char *, const char *,...)
char * ztrdup(const char *)