#ifndef CST_SANDBOX_H #define CST_SANDBOX_H /* * OS sandbox for run_shell: reads and exec stay allowed everywhere, writes * are limited to the workspace, the temp dir, /dev/null and /dev/tty, and * the network is cut where the OS can do it. * Linux Landlock (network needs ABI 4, kernel 6.7+) * Linux seccomp user-notify when Landlock is missing (Android/Termux, * older kernels): a supervisor thread checks the path of every * write-type syscall and IPv4/IPv6 sockets are refused. Guards * against mistakes, not hostile code (the path is checked before * the kernel reads it again). * macOS sandbox_init (Seatbelt profile) * Windows Low integrity level: a restricted token (no admin privileges, * Low IL) can only write objects labeled Low, so the workspace and * a temp dir get that label; the credentials file is labeled * no-read-up. The network stays open (callers ask first). * other none: callers fall back to rule-based approval */ #include "os_compat.h" #include #ifdef __cplusplus extern "C" { #endif typedef enum { CST_SANDBOX_NONE = 0, CST_SANDBOX_LANDLOCK, CST_SANDBOX_SEATBELT, CST_SANDBOX_SECCOMP, CST_SANDBOX_LOWIL } cst_sandbox_kind_t; /* What this process can use; cached. CST_SANDBOX=off|0|none forces NONE, * CST_SANDBOX=landlock|seccomp|lowil allows only that kind. */ int cst_sandbox_probe(void); /* Drops the cache and probes again (tests that change CST_SANDBOX). */ int cst_sandbox_reprobe(void); /* "landlock" | "seatbelt" | "seccomp" | "lowil" | "none" */ const char *cst_sandbox_name(int kind); /* Landlock ABI version (0 when unavailable); ≥4 also blocks TCP. */ int cst_sandbox_landlock_abi(void); typedef struct { int kind; int ruleset_fd; /* Landlock, built in the parent */ char *profile; /* Seatbelt */ void *sc; /* seccomp: filter program and supervisor thread */ void *token; /* lowil: primary token for CreateProcessAsUserW */ char *tmpdir; /* lowil: Low-labeled TEMP for the child */ char *note; /* lowil: shown once when a directory was just labeled */ } cst_sandbox_plan_t; /* Parent side: build the policy. Returns 0, or -1 (plan->kind = NONE). */ int cst_sandbox_prepare(const char *workspace, const char *tmpdir, cst_sandbox_plan_t *plan); /* Child side, after fork and before exec (os_run_capture_ex hook): * async-signal-safe. Returns 0 when confined. */ int cst_sandbox_enter(void *plan); void cst_sandbox_release(cst_sandbox_plan_t *plan); /* `sh -c cmd` confined to workspace (+ temp dir). Fails closed: if the * policy cannot be applied the command does not run (exit 126). */ int cst_sandbox_run_shell(const char *workspace, const char *cmd, size_t max_out, os_run_result_t *r); /* Output of a failed sandboxed run that looks like the sandbox got in the * way (EACCES / EPERM / EROFS / blocked network). */ int cst_sandbox_blocked(const char *out, size_t n, int exit_code); /* lowil: directories this user's cst-agent labeled Low (recorded in the * config dir), one per line; malloc'd, NULL when there are none. */ char *cst_sandbox_labels(void); /* Gives every recorded directory its old label back. Returns how many were * restored (-1 when some failed; those stay recorded); *report gets a * malloc'd summary. */ int cst_sandbox_reset_labels(char **report); #ifdef __cplusplus } #endif #endif