aboutsummaryrefslogtreecommitdiffstats
path: root/FICS/prep_dir_for_privdrop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'FICS/prep_dir_for_privdrop.cpp')
-rw-r--r--FICS/prep_dir_for_privdrop.cpp158
1 files changed, 158 insertions, 0 deletions
diff --git a/FICS/prep_dir_for_privdrop.cpp b/FICS/prep_dir_for_privdrop.cpp
new file mode 100644
index 0000000..4d4478c
--- /dev/null
+++ b/FICS/prep_dir_for_privdrop.cpp
@@ -0,0 +1,158 @@
+// SPDX-FileCopyrightText: 2025 Markus Uhlin <maxxe@rpblc.net>
+// SPDX-License-Identifier: ISC
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <string>
+
+#include "addgroup.h"
+#include "prep_dir_for_privdrop.h"
+#include "settings.h"
+#include "utils.h"
+
+namespace fs = std::filesystem;
+
+static int
+get_uid_and_gid(uid_t &uid, gid_t &gid)
+{
+ struct passwd *pw = nullptr;
+ int i = 0;
+
+ if ((pw = getpwnam(settings_get("privdrop_user"))) == nullptr ||
+ !get_group_id(settings_get("sysgroup"), &i)) {
+ uid = 0;
+ gid = 0;
+ return -1;
+ }
+
+ uid = pw->pw_uid;
+ gid = static_cast<gid_t>(i);
+ return 0;
+}
+
+static int
+check_prep_done(const char *p_path)
+{
+ std::string path(p_path);
+
+ path.append("/").append(".prep_done");
+
+ if (file_exists(path.c_str()))
+ return 0;
+ return -1;
+}
+
+static void
+prep_done(const char *p_path)
+{
+ int fd;
+ std::string path(p_path);
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ path.append("/").append(".prep_done");
+
+ fd = open(path.c_str(), (O_RDWR|O_CREAT|O_TRUNC), (S_IRUSR|S_IWUSR|
+ S_IRGRP|S_IROTH));
+ if (fd < 0) {
+ warn("%s: open", __func__);
+ return;
+ }
+
+ dprintf(fd, "FICS dir preparation done.\n"
+ "Do not remove this file while FICS is installed.\n");
+ close(fd);
+
+ if (get_uid_and_gid(uid, gid) == 0) {
+ if (chown(path.c_str(), uid, gid) != 0)
+ warn("%s: chown", __func__);
+ }
+}
+
+int
+drop_root_privileges(const char *path)
+{
+ struct passwd *pw;
+
+ if ((pw = getpwnam(settings_get("privdrop_user"))) == nullptr) {
+ warnx("%s: password database search failed", __func__);
+ return -1;
+ } else if (chdir(path) != 0) {
+ warn("%s: chdir(%s)", __func__, path);
+ return -1;
+ } else if (setgid(pw->pw_gid) == -1) {
+ warn("%s: setgid", __func__);
+ return -1;
+ } else if (setegid(pw->pw_gid) == -1) {
+ warn("%s: setegid", __func__);
+ return -1;
+ } else if (setuid(pw->pw_uid) == -1) {
+ warn("%s: setuid", __func__);
+ return -1;
+ } else if (seteuid(pw->pw_uid) == -1) {
+ warn("%s: seteuid", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+prep_dir_for_privdrop(const char *path)
+{
+ if (path == nullptr || strcmp(path, "") == 0)
+ return -1;
+ else if (check_prep_done(path) == 0)
+ return 0;
+ try {
+ fs::path v_path = path;
+ fs::recursive_directory_iterator dir_it(v_path);
+ uid_t uid = 0;
+ gid_t gid = 0;
+ constexpr mode_t dir_mode = (S_IRWXU|S_IRGRP|S_IXGRP);
+ constexpr mode_t file_mode = (S_IRUSR|S_IWUSR|S_IRGRP);
+
+ if (get_uid_and_gid(uid, gid) == -1) {
+ throw std::runtime_error("failed to get uid/gid");
+ } else if (chown(path, uid, gid) != 0) {
+ std::string str("chown() error: ");
+ str.append(strerror(errno));
+ throw std::runtime_error(str);
+ } else if (chmod(path, dir_mode) != 0) {
+ std::string str("chmod() error: ");
+ str.append(strerror(errno));
+ throw std::runtime_error(str);
+ }
+
+ for (auto const &dir_ent : dir_it) {
+ const std::string str(dir_ent.path().string());
+
+ if (chown(str.c_str(), uid, gid) != 0) {
+ warn("%s: chown(%s, ...)", __func__,
+ str.c_str());
+ }
+
+ if (dir_ent.is_directory()) {
+ if (chmod(str.c_str(), dir_mode) != 0)
+ warn("%s: chmod", __func__);
+ } else if (dir_ent.is_regular_file()) {
+ if (chmod(str.c_str(), file_mode) != 0)
+ warn("%s: chmod", __func__);
+ }
+ }
+ } catch (const std::exception &ex) {
+ warnx("%s: %s", __func__, ex.what());
+ return -1;
+ }
+
+ prep_done(path);
+ return 0;
+}