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.
 
 
 
 
 
 

187 lines
6.0 KiB

// Fake sudo using user namespace; Similar to fakeroot
// (c) 2020 Taeyeon Mori <taeyeon at oro.sodimm.me>
#include "kons.hpp"
#include <cstdlib>
#include <iostream>
#include <unistd.h>
#include <getopt.h>
#include <pwd.h>
#include <grp.h>
// Helpers
int xerror(const char *desc) {
perror(desc);
return -errno;
}
[[noreturn]] void die(int r, const char *msg) {
std::cerr << msg << std::endl;
exit(r);
}
[[noreturn]] void die_errno(int r, const char *msg) {
perror(msg);
exit(r);
}
// ========================================================
// Main
// ========================================================
void usage(const char *prog) {
std::cout << "Usage:" << std::endl
<< " " << prog << " -h | -K | -k | -V" << std::endl
<< " " << prog << " -v [-k] [-u user] [-g group]" << std::endl
<< " " << prog << " -e [-k] [-u user] [-g group] [--] file" << std::endl
<< " " << prog << " [-bEHPk] [-u user] [-g group] [-i|-s] [--] command" << std::endl
<< std::endl
<< "General Options:" << std::endl
<< " -h Display this help text" << std::endl
<< std::endl;
}
struct config {
const char *const *exec_argv = nullptr;
bool background = false,
preserve_env = false,
editor = false,
login = false,
set_home = false,
preserve_groups = false,
run_shell = false;
uid_t uid = 0;
gid_t gid = 0;
};
template <typename F, typename R, typename T>
R get_pwd(F f, R std::remove_pointer_t<std::invoke_result_t<F,T>>::*fld, T nam) {
auto s = f(nam);
if (!s)
die(20, "Could not resolve user or group");
return s->*fld;
}
// Parse commandline arguments
// returns -1 on success, exit code otherwise
int parse_cmdline(config &conf, int argc, const char *const *argv) {
constexpr auto spec = "+hbEeg:HiKkPpsu:Vv";
constexpr option longspec[] = {{"help",0,nullptr,'h'},
{"background",0,nullptr,'b'},
{"preserve-env",2,nullptr,'E'},
{"edit",0,nullptr,'e'},
{"group",1,nullptr,'g'},
{"set-home",0,nullptr,'H'},
{"login",0,nullptr,'i'},
{"remove-timestamp",0,nullptr,'K'},
{"reset-timestamp",0,nullptr,'k'},
{"preserve-groups",0,nullptr,'P'},
{"prompt",1,nullptr,'p'},
{"shell",0,nullptr,'s'},
{"user",1,nullptr,'u'},
{"version",0,nullptr,'V'},
{"validate",0,nullptr,'v'},
{nullptr,0,nullptr,0}};
while (true) {
auto opt = getopt_long(argc, const_cast<char *const *>(argv), spec, longspec, nullptr);
if (opt == -1)
break;
else if (opt == '?' || opt == 'h') {
usage(argv[0]);
return opt == 'h' ? 0 : 1;
}
else if (opt == 'V') {
std::cout << "fakensudo Namespace fake sudo version 0.1" << std::endl
<< "(c) 2020 Taeyeon Mori" << std::endl;
return 0;
}
else if (opt == 'b') conf.background = true;
else if (opt == 'E') conf.preserve_env = true; // XXX: ignores the optinal list
else if (opt == 'e') conf.editor = true;
else if (opt == 'g') conf.gid = get_pwd(getgrnam, &group::gr_gid, optarg);
else if (opt == 'H') conf.set_home = true;
else if (opt == 'i') conf.login = true;
else if (opt == 'K') return 0; // XXX: check for clashes
else if (opt == 'k') /* pass */;
else if (opt == 'P') conf.preserve_groups = true;
else if (opt == 'p') /* pass */;
else if (opt == 's') conf.run_shell = true;
else if (opt == 'u') conf.uid = get_pwd(getpwnam, &passwd::pw_uid, optarg);
else if (opt == 'v') return 0; // XXX: properly check options
else die(10, "Unknown option encountered");
}
// Check sanity
bool good = true;
if (conf.run_shell || conf.login) {
if (conf.run_shell && conf.login)
good = false;
if (conf.editor)
good = false;
} else if (::optind >= argc)
good = false;
if (!good) {
usage(argv[0]);
return 5;
}
// Rest is child cmnd
if (argc > ::optind)
conf.exec_argv = &argv[::optind];
return -1;
}
int main(int argc, char **argv) {
// Set defaults
auto conf = config{};
// Parse commandline
auto perr = parse_cmdline(conf, argc, argv);
if (perr != -1)
return perr;
auto uerr = ko::ns::unshare_single(conf.uid, conf.gid, CLONE_NEWUSER);
if (uerr != 0)
die_errno(31, "unshare");
// Drop Permissions
setresgid(conf.gid, conf.gid, conf.gid);
setresuid(conf.uid, conf.uid, conf.uid);
auto exec_argv = conf.exec_argv;
if (conf.run_shell) {
auto shell = getenv("SHELL");
if (shell == nullptr)
die(41, "Could not get SHELL from environment");
if (conf.exec_argv == nullptr || *conf.exec_argv == nullptr)
exec_argv = new char*[]{shell, nullptr};
else
die(200, "-s not fully implemented");
} else if (conf.login) {
auto shell = get_pwd(getpwuid, &passwd::pw_shell, conf.uid);
if (shell == nullptr)
die(41, "Could not get SHELL from passwd record");
if (conf.exec_argv == nullptr || *conf.exec_argv == nullptr)
exec_argv = new char*[]{shell, "-l", nullptr};
else
die(200, "-i not fully implemented");
} else if (conf.editor) {
die(200, "-e not implemented");
}
// Exec
execvpe(exec_argv[0], const_cast<char *const*>(exec_argv), environ);
die_errno(33, "exec");
}