Dotfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
4.8 KiB

#include <iostream>
#include <filesystem>
#include <unordered_set>
#include <fstream>
#include "koutil.hpp"
#include "kofd.hpp"
#include "koos.hpp"
namespace fs = std::filesystem;
void usage(const char *prog) {
std::cout << "Usage: " << prog << " [option...] <newhome> [prog] [arg...]" << std::endl
<< " (c) 2019 Taeyeon Mori" << std::endl
<< std::endl
<< " This program allows confining an application to it's own home directory" << std::endl
<< " without chainging the literal home directory path." << std::endl
<< std::endl
<< "Options:" << std::endl
<< " -h Display this help text" << std::endl
<< " -H HOME Override the home directory path" << std::endl
<< " -w Don't make / read-only" << std::endl
<< " -W Preserve working directory" << std::endl
//<< " -s Make (the rest of) /home inaccessible" << std::endl
//<< " -S Make /media and /mnt inaccessible as well (implies -s)" << std::endl
//<< " -x PATH Make path inaccessible" << std::endl
<< std::endl
<< "Parameters:" << std::endl
<< " newhome The new home directory path" << std::endl
<< " prog The executable to run (defaults to $SHELL)" << std::endl
<< " arg... The executable parameters" << std::endl;
}
struct params {
fs::path home, newhome;
bool rw = false,
nohome = false,
nomnt = false,
pwd = true;
std::unordered_set<std::string> hide;
const char *const *argv = nullptr;
};
int bindfile(const params &p, fs::path path) {
auto opath = p.home / path;
if (fs::exists(opath)) {
auto npath = p.newhome / path;
if (!fs::exists(npath)) {
if (fs::is_directory(opath))
fs::create_directories(npath);
else {
fs::create_directories(npath.parent_path());
auto touch = std::ofstream(npath);
}
}
if(ko::os::bind(opath, npath, 0))
return -1;
return ko::os::bind(npath, npath, MS_REMOUNT|MS_RDONLY);
}
return 0;
}
int pmain(params p) {
auto uid = getuid(),
gid = getgid();
auto [e, eloc] = ko::util::cvshort()
.then("unshare", ::unshare, CLONE_NEWUSER|CLONE_NEWNS)
.then("bind Xauthority", bindfile, p, ".Xauthority")
.then("bind pulse cookie", bindfile, p, ".config/pulse/cookie")
.then("bind home", ko::os::bind, p.newhome, p.home, MS_REC)
.ifthen("make / ro", !p.rw, ko::os::bind, "/", "/", MS_REMOUNT|MS_RDONLY)
.ifthen("chdir", p.pwd, ::chdir, p.home.c_str())
.then([uid,gid]() -> ko::util::cvresult {
auto dir = ko::fd::opendir("/proc/self");
if (!dir)
return {-1, "open /proc/self"};
if (!ko::fd::dump("deny", "setgroups", 0644, dir))
return {-1, "write setgroups"};
if (!ko::fd::dump(ko::util::str(gid, " ", gid, " 1\n"), "gid_map", 0644, dir))
return {-1, "write gid_map"};
if (!ko::fd::dump(ko::util::str(uid, " ", uid, " 1\n"), "uid_map", 0644, dir))
return {-1, "write uid_map"};
return {0, nullptr};
})
.then("setresgid", ::setresgid, gid, gid, gid)
.then("setresuid", ::setresuid, uid, uid, uid)
.then("exec", ::execvp, p.argv[0], const_cast<char *const *>(p.argv));
perror(eloc);
return e;
}
int main(int argc, char **argv) {
static const char *exec_argv[] = {getenv("SHELL"), nullptr};
params p{
.home = ko::os::get_home(),
.argv = exec_argv
};
constexpr auto spec = "+hH:wsSxW";
while (true) {
auto opt = getopt(argc, const_cast<char *const *>(argv), spec);
if (opt == -1)
break;
else if (opt == '?' || opt == 'h') {
usage(argv[0]);
return opt == 'h' ? 0 : 1;
}
else if (opt == 'H')
p.home = ::optarg;
else if (opt == 'w')
p.rw = true;
else if (opt == 'W')
p.pwd = false;
else if (opt == 's' || opt == 'S') {
p.hide.emplace("/home");
if (opt == 'S') {
p.hide.emplace("/media");
p.hide.emplace("/mnt");
}
}
else if (opt == 'x')
p.hide.emplace(::optarg);
}
if (argc == ::optind) {
std::cout << "Error: missing mandatory newhome argument, see `" << argv[0] << " -h`" << std::endl;
return 2;
}
p.newhome = argv[::optind++];
if (argc > ::optind)
p.argv = &argv[::optind];
return pmain(p);
}