Repositories / jai.git

cred.h

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

Branch
2896 bytes · c04c255e599f
// -*-C++-*- #pragma once #include "err.h" #include <format> #include <string> #include <type_traits> #include <typeinfo> #include <utility> #include <vector> #include <errno.h> #include <grp.h> #include <pwd.h> #include <sys/types.h> #include <unistd.h> // Something that can contain a user or group id using ugid_t = std::common_type_t<uid_t, gid_t>; // Wrapper around getpw{nam,uid}_r and getgr{nam,gid}_id template<typename Ent, auto IdFn, auto NamFn> struct DbEnt { Ent *p_{}; Ent ent_; std::vector<char> buf_; DbEnt() noexcept = default; DbEnt(DbEnt &&other) : ent_(other.ent_), buf_(exchange(other.buf_, {})) { p_ = other.p_ ? &ent_ : nullptr; other.p_ = nullptr; } DbEnt &operator=(DbEnt &&other) noexcept { p_ = other.p_ ? &ent_ : nullptr; other.p_ = nullptr; ent_ = other.ent_; buf_ = std::exchange(other.buf_, {}); return *this; } explicit operator bool() const { return p_; } const Ent *operator->() const { return p_; } Ent *get() const { return p_; } static DbEnt get_id(ugid_t n) { return find(IdFn, n); } static DbEnt get_nam(const char *n) { return find(NamFn, n); } static DbEnt find(auto fn, auto key) { DbEnt ret; ret.buf_.resize(std::max(128uz, ret.buf_.capacity())); for (;;) { int r = fn(key, &ret.ent_, ret.buf_.data(), ret.buf_.size(), &ret.p_); if (!r) return ret ? std::move(ret) : DbEnt{}; else if (r == ERANGE) ret.buf_.resize(2 * ret.buf_.size()); else if (r == ENOENT) return DbEnt{}; else errno = r, syserr("DbEnt<{}>::find", typeid(Ent).name()); } } }; using PwEnt = DbEnt<passwd, getpwuid_r, getpwnam_r>; using GrEnt = DbEnt<group, getgrgid_r, getgrnam_r>; struct Credentials { uid_t uid_ = -1; gid_t gid_ = -1; std::vector<gid_t> groups_; void make_effective() const; void make_real() const; std::string show() const; explicit operator bool() const noexcept { return uid_ != -1; } static Credentials get_user(const struct passwd *pw); static Credentials get_user(const PwEnt &e) { return get_user(e.get()); } static Credentials get_effective() { return Credentials{ .uid_ = geteuid(), .gid_ = getegid(), .groups_ = getgroups(), }; } static Credentials get_real() { return Credentials{ .uid_ = getuid(), .gid_ = getgid(), .groups_ = getgroups(), }; } static std::vector<gid_t> getgroups(); friend bool operator==(const Credentials &, const Credentials &) noexcept = default; }; template<> struct std::formatter<Credentials> : std::formatter<std::string_view> { using super = std::formatter<std::string_view>; auto format(const Credentials &creds, auto &ctx) const { return super::format(creds.show(), ctx); } }; std::string make_id_map(ugid_t user, ugid_t untrusted);