Skip to content
API Documentation
BastionGuard™
PolicySecurity.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <cerrno>
5#include <cstring>
6#include <fcntl.h>
7#include <stdexcept>
8#include <string>
9#include <sys/stat.h>
10#include <unistd.h>
11
13namespace {
14
15constexpr std::size_t kMaximumPolicyBytes = 1024U * 1024U;
16
17class UniqueFd final {
18public:
19 explicit UniqueFd(const int value) noexcept : value_(value) {}
20 ~UniqueFd() { if (value_ >= 0) ::close(value_); }
21 UniqueFd(const UniqueFd&) = delete;
22 UniqueFd& operator=(const UniqueFd&) = delete;
23 [[nodiscard]] int get() const noexcept { return value_; }
24private:
25 int value_;
26};
27
28[[nodiscard]] std::runtime_error systemError(const char* operation)
29{
30 return std::runtime_error(std::string(operation) + ": " +
31 std::strerror(errno));
32}
33
34} // namespace
35
36std::string PolicySecurity::read(const std::filesystem::path& path,
37 const bool requireSecureOwnership)
38{
39 const int rawFd = ::open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
40 if (rawFd < 0)
41 throw systemError("open policy");
42 const UniqueFd fd(rawFd);
43
44 struct stat status {};
45 if (::fstat(fd.get(), &status) != 0)
46 throw systemError("fstat policy");
47
48 if (!S_ISREG(status.st_mode))
49 throw std::runtime_error("la policy deve essere un file regolare");
50 if (status.st_size < 0 ||
51 static_cast<std::uintmax_t>(status.st_size) > kMaximumPolicyBytes) {
52 throw std::runtime_error("la policy supera il limite di 1 MiB");
53 }
54
55 if (requireSecureOwnership) {
56 if (status.st_uid != 0)
57 throw std::runtime_error("la policy deve appartenere a root");
58 if ((status.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
59 throw std::runtime_error(
60 "la policy non deve essere scrivibile da gruppo o altri");
61 }
62 }
63
64 std::string content;
65 content.reserve(static_cast<std::size_t>(status.st_size));
66 char buffer[4096];
67 for (;;) {
68 const ssize_t count = ::read(fd.get(), buffer, sizeof(buffer));
69 if (count == 0)
70 break;
71 if (count < 0) {
72 if (errno == EINTR)
73 continue;
74 throw systemError("read policy");
75 }
76 if (content.size() + static_cast<std::size_t>(count) >
77 kMaximumPolicyBytes) {
78 throw std::runtime_error("la policy supera il limite di 1 MiB");
79 }
80 content.append(buffer, static_cast<std::size_t>(count));
81 }
82
83 struct stat finalStatus {};
84 if (::fstat(fd.get(), &finalStatus) != 0)
85 throw systemError("fstat policy dopo lettura");
86 if (finalStatus.st_dev != status.st_dev ||
87 finalStatus.st_ino != status.st_ino ||
88 finalStatus.st_size != status.st_size ||
89 finalStatus.st_mtim.tv_sec != status.st_mtim.tv_sec ||
90 finalStatus.st_mtim.tv_nsec != status.st_mtim.tv_nsec ||
91 finalStatus.st_ctim.tv_sec != status.st_ctim.tv_sec ||
92 finalStatus.st_ctim.tv_nsec != status.st_ctim.tv_nsec) {
93 throw std::runtime_error("la policy è cambiata durante la lettura");
94 }
95
96 return content;
97}
98
99} // namespace bastionguard::rootguard
static std::string read(const std::filesystem::path &path, bool requireSecureOwnership)
#define S_IWOTH
#define S_IWGRP