#include "access_token.h" #include "elevation.h" #include #include #include #include #include #include #include #include namespace raven { std::optional token_access(uint32_t requested) { if (requested & ~0xf20f01ffU) return std::nullopt; uint32_t access = requested & 0xf01ff; if (requested & (0x10000000U | 0x02000000U)) access |= 0xf01ff; if (requested & 0x80000000U) access |= 0x20008; if (requested & 0x40000000U) access |= 0x200e0; if (requested & 0x20000000U) access |= 0x20000; return access; } struct AccessToken::Shared { uint32_t integrity, type, acl_size, impersonation_level; uint32_t privilege_count; Privilege privileges[64]; uint8_t acl[65535]; }; namespace { class TokenLock { public: TokenLock(int descriptor, bool write) : descriptor(descriptor) { struct flock lock{}; lock.l_type = write ? F_WRLCK : F_RDLCK; lock.l_whence = SEEK_SET; int result; do result = ::fcntl(descriptor, F_SETLKW, &lock); while (result < 0 && errno == EINTR); if (result < 0) throw std::runtime_error("cannot lock access token storage"); } ~TokenLock() { struct flock lock{}; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; ::fcntl(descriptor, F_SETLK, &lock); } private: int descriptor; }; } void AccessToken::map() { struct stat information{}; if (descriptor < 0 || ::fstat(descriptor, &information) || information.st_size != sizeof(Shared)) throw std::runtime_error("invalid access token storage"); const auto mapped = ::mmap(nullptr, sizeof(Shared), PROT_READ | PROT_WRITE, MAP_SHARED, descriptor, 0); if (mapped == MAP_FAILED) throw std::runtime_error("cannot map access token storage"); shared = static_cast(mapped); } AccessToken::AccessToken() { descriptor = ::memfd_create("raven-token", MFD_CLOEXEC | MFD_ALLOW_SEALING); try { if (descriptor < 0 || ::ftruncate(descriptor, sizeof(Shared)) || ::fcntl(descriptor, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL)) throw std::runtime_error("cannot create access token storage"); map(); // Medium integrity, or high in a process that runs elevated, as an administrator's full token is. shared->integrity = process_elevated() ? 12288 : 8192; shared->type = 1; shared->impersonation_level = 2; shared->privilege_count = 1; shared->privileges[0] = {23, 3}; } catch (...) { if (descriptor >= 0) ::close(descriptor); throw; } } AccessToken::AccessToken(int descriptor) : descriptor(descriptor) { try { map(); } catch (...) { if (descriptor >= 0) ::close(descriptor); throw; } } AccessToken::AccessToken(const AccessToken &other) : AccessToken() { const auto value = other.data(); shared->integrity = value.integrity; shared->type = value.type; shared->impersonation_level = value.impersonation_level; shared->acl_size = uint32_t(value.default_dacl.size()); shared->privilege_count = uint32_t(value.privileges.size()); std::copy(value.privileges.begin(), value.privileges.end(), shared->privileges); if (!value.default_dacl.empty()) std::memcpy(shared->acl, value.default_dacl.data(), value.default_dacl.size()); } AccessToken::~AccessToken() { if (shared) ::munmap(shared, sizeof(Shared)); if (descriptor >= 0) ::close(descriptor); } bool AccessToken::describe(InheritedHandle &record) const { record.kind = "token"; record.descriptor = descriptor; return true; } AccessToken::Data AccessToken::data() const { const TokenLock lock(descriptor, false); if (shared->acl_size > sizeof(shared->acl) || shared->privilege_count > std::size(shared->privileges)) throw std::runtime_error("invalid access token storage size"); return {{shared->acl, shared->acl + shared->acl_size}, shared->integrity, shared->type, shared->impersonation_level, {shared->privileges, shared->privileges + shared->privilege_count}}; } void AccessToken::set_default_dacl(std::span acl) { if (acl.size() > sizeof(shared->acl)) throw std::runtime_error("access token ACL exceeds storage"); const TokenLock lock(descriptor, true); if (!acl.empty()) std::memcpy(shared->acl, acl.data(), acl.size()); shared->acl_size = uint32_t(acl.size()); } void AccessToken::set_integrity(uint32_t value) { const TokenLock lock(descriptor, true); shared->integrity = value; } void AccessToken::set_type(uint32_t value) { const TokenLock lock(descriptor, true); shared->type = value; } void AccessToken::set_impersonation_level(uint32_t value) { const TokenLock lock(descriptor, true); shared->impersonation_level = value; } void AccessToken::restrict_privileges(bool maximum, std::span removed) { const TokenLock lock(descriptor, true); if (shared->privilege_count > std::size(shared->privileges)) throw std::runtime_error("invalid access token privilege count"); uint32_t count = 0; for (uint32_t index = 0; index < shared->privilege_count; ++index) { const auto privilege = shared->privileges[index]; if (maximum ? privilege.luid != 23 : std::find(removed.begin(), removed.end(), privilege.luid) != removed.end()) continue; shared->privileges[count++] = privilege; } shared->privilege_count = count; } uint32_t AccessToken::adjust_privileges(bool disable, std::span changes, const std::function)> &prepare) { const TokenLock lock(descriptor, true); if (shared->privilege_count > std::size(shared->privileges)) throw std::runtime_error("invalid access token privilege count"); const std::vector original(shared->privileges, shared->privileges + shared->privilege_count); auto next = original; bool assigned = true; if (disable) { for (auto &privilege : next) privilege.attributes &= ~2U; } else { for (const auto &change : changes) { const auto found = std::find_if(next.begin(), next.end(), [&](const auto &privilege) { return privilege.luid == change.luid; }); if (found == next.end()) { assigned = false; continue; } if (change.attributes & 4) next.erase(found); else found->attributes = (found->attributes & ~2U) | (change.attributes & 2); } } std::vector previous; for (const auto &privilege : original) { const auto found = std::find_if(next.begin(), next.end(), [&](const auto &entry) { return entry.luid == privilege.luid; }); if (found != next.end() && found->attributes != privilege.attributes) previous.push_back(privilege); } if (const auto error = prepare(previous)) return error; std::copy(next.begin(), next.end(), shared->privileges); shared->privilege_count = uint32_t(next.size()); return assigned ? 0 : 1300; } }