Repositories / jai.git

jai.h

Clone (read-only): git clone http://git.guha-anderson.com/git/jai.git

Branch
6047 bytes · 1195ae3c9ce9
// -*-C++-*- #pragma once #include "config.h" #include "cred.h" #include "err.h" #include "fs.h" #include "options.h" #include <linux/sched.h> #include <sched.h> #include <signal.h> #include <sys/syscall.h> #include <unistd.h> extern "C" char **environ; inline const char *env_or_empty(std::string_view var) { const char *p = getenv(std::string(var).c_str()); return p ? p : ""; } // Calls exp("VAR"sv) to expand strings like "123${VAR}456". template <typename Exp = decltype(env_or_empty)> std::string var_expand(std::string_view in, Exp &&exp = env_or_empty) requires requires(std::string r) { r += exp(in); } { std::string ret; for (std::size_t i = 0, e = in.size(); i < e;) if (in[i] == '\\') ret += (++i < e ? in[i++] : '\\'); else if (size_t j; in.substr(i, 2) == "${" && (j = in.find('}', i + 2)) != in.npos) { ret += exp(in.substr(i + 2, j - i - 2)); i = j + 1; } else ret += in[i++]; return ret; } inline pid_t xfork(std::uint64_t flags = 0) { clone_args ca{.flags = flags, .exit_signal = SIGCHLD}; if (auto ret = syscall(SYS_clone3, &ca, sizeof(ca)); ret != -1) return ret; syserr("clone3"); } inline sigset_t sigsingleton(int sig) { sigset_t ret; sigemptyset(&ret); sigaddset(&ret, sig); return ret; } #define xsetns(fd, type) \ do { \ if (setns(fd, type)) { \ warn("setns({}, {}): {}", fdpath(fd), #type, strerror(errno)); \ exit(1); \ } \ } while (0) constexpr const char *kUntrustedUser = UNTRUSTED_USER; constexpr const char *kUntrustedGecos = "JAI sandbox untrusted user"; constexpr const char *kRunRoot = "/run/jai"; extern const std::string jai_defaults; extern const std::string default_conf; extern const std::string default_jail; struct Config { enum Mode { kCasual, kBare, kStrict }; Mode mode_{kStrict}; PathSet grant_directories_; bool grant_cwd_{true}; std::set<std::string, std::less<>> env_filter_; std::map<std::string, std::string, std::less<>> setenv_; path cwd_; std::string shellcmd_; PathSet mask_files_; bool mask_warn_{}; bool parsing_config_file_{}; std::string user_; path homepath_; path homejaipath_; path storagedir_; path sandbox_name_; Credentials user_cred_; Credentials untrusted_cred_; path shell_; mode_t old_umask_ = 0755; Fd home_fd_; Fd home_jai_fd_; Fd storage_fd_; Fd run_jai_fd_; Fd run_jai_user_fd_; // Hold some file descriptors to prevent unmounting std::vector<Fd> mp_holder_; PathSet config_loop_detect_; void init_credentials(); Fd make_idmap_ns(); Fd make_mnt_ns(); void exec(int nsfd, char **argv); void unmount(); bool unmountall(); std::unique_ptr<Options> opt_parser(bool dotjail = false); int complete(Options::Completions c); void parse_config_fd(int fd, Options *opts = nullptr); bool parse_config_file(path file, Options *opts = nullptr); std::vector<const char *> make_env(); static void fix_proc(); [[noreturn]] static void parent_loop(pid_t jai_init_pid, int stop_requests); void static pid1(Fd stop_me); [[noreturn]] void pid2(char **argv); [[nodiscard]] static Defer asuser(const Credentials *crp); [[nodiscard]] Defer asuser() { return asuser(&user_cred_); } void check_user(const struct stat &sb, std::string path_for_error = {}, bool untrusted_ok = false); void check_user(int fd, std::string path_for_error = {}, bool untrusted_ok = false) { check_user(xfstat(fd), path_for_error.empty() ? fdpath(fd) : path_for_error, untrusted_ok); } Fd ensure_udir(int dfd, const path &p, mode_t perm = 0700, FollowLinks follow = kFollow) { auto _restore = asuser(); Fd fd = ensure_dir(dfd, p, perm, follow); check_user(*fd); return fd; } int home(); int home_jai(bool create = false); int storage(); int run_jai(); int run_jai_user(); const path &cwd() { if (cwd_.empty()) { auto restore = asuser(); cwd_ = canonical(std::filesystem::current_path()); } return cwd_; } Fd make_blacklist(int dfd, path name); Fd make_home_overlay(); Fd make_private_tmproot(); Fd make_private_tmp(path subdir = {}, bool userowned = false); Fd make_private_passwd(); const char *env_lookup(std::string_view var) { if (auto it = setenv_.find(var); it != setenv_.end()) if (auto pos = it->second.find('='); pos != it->second.npos) return it->second.c_str() + pos + 1; return env_or_empty(var); } std::string expand(std::string_view in) { return parsing_config_file_ ? var_expand( in, [this](std::string_view v) { return env_lookup(v); }) : std::string(in); } static bool name_ok(path p) { return p.is_relative() && std::ranges::distance(p.begin(), p.end()) == 1 && *p.c_str() != '.'; } void mask_warn() { if (mask_warn_) { warn(R"(--mask ignored because {5}/{0}/{1}.home already mounted. {2:>{3}} Run "{4} -u" to unmount overlays.)", user_, sandbox_name_.string(), "", prog.filename().string().size(), prog.filename().string(), kRunRoot); mask_warn_ = false; } } }; template <> struct std::formatter<Config::Mode> : std::formatter<const char *> { using super = std::formatter<const char *>; auto format(Config::Mode m, auto &&ctx) const { using enum Config::Mode; switch (m) { case kStrict: return super::format("strict", ctx); case kBare: return super::format("bare", ctx); case kCasual: return super::format("casual", ctx); default: err<std::logic_error>("Config::Mode with bad value {}", int(m)); } } };