--- /dev/null
+/*#############################################################################
+# #
+# telemetryd - The IPFire Telemetry Collection Service #
+# Copyright (C) 2026 IPFire Development Team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#include <errno.h>
+#include <grp.h>
+#include <linux/prctl.h>
+#include <pwd.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "ctx.h"
+#include "priv.h"
+
+static int td_priv_cap_acquire_setpcap(td_ctx* ctx) {
+ cap_flag_value_t value;
+ int r;
+
+ // Fetch current capabilities
+ cap_t caps = cap_get_proc();
+
+ // Check if CAP_SETPCAP is already enabled
+ r = cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &value);
+ if (r) {
+ ERROR(ctx, "The kernel does not seem to know CAP_SETPCAP: %m\n");
+ goto ERROR;
+ }
+
+ // It CAP_SETPCAP isn't set, we will try to set it
+ if (value != CAP_SET) {
+ const cap_value_t cap = CAP_SETPCAP;
+
+ r = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
+ if (r) {
+ ERROR(ctx, "Could not set CAP_SETPCAP: %m\n");
+ goto ERROR;
+ }
+
+ // Store capabilities
+ r = cap_set_proc(caps);
+ if (r) {
+ ERROR(ctx, "Could not acquire effective CAP_SETPCAP capability: %m\n");
+ goto ERROR;
+ }
+ }
+
+ERROR:
+ if (caps)
+ cap_free(caps);
+
+ return r;
+}
+
+static cap_flag_value_t td_priv_keep_cap(const cap_value_t cap, const cap_value_t* keep_caps) {
+ for (const cap_value_t* c = keep_caps; *c; c++) {
+ if (cap == *c)
+ return CAP_SET;
+ }
+
+ return CAP_CLEAR;
+}
+
+static int td_priv_drop_capabilities(td_ctx* ctx) {
+ int r;
+
+ const cap_value_t keep_caps[] = {
+ CAP_DAC_OVERRIDE,
+ CAP_NET_ADMIN,
+ CAP_NET_RAW,
+ CAP_SYS_ADMIN,
+ 0,
+ };
+
+ // Acquire CAP_SETPCAP
+ r = td_priv_cap_acquire_setpcap(ctx);
+ if (r)
+ return r;
+
+ // Fetch the current set of capabilities
+ cap_t caps = cap_get_proc();
+
+ // Drop all capabilities that we do not need
+ for (cap_value_t cap = 1; cap <= CAP_LAST_CAP; cap++) {
+ // Skip any capabilities we would like to skip
+ cap_flag_value_t flag = td_priv_keep_cap(cap, keep_caps);
+
+ // Drop the capability from the bounding set
+ if (flag == CAP_CLEAR) {
+ r = prctl(PR_CAPBSET_DROP, cap);
+ if (r) {
+ ERROR(ctx, "Could not drop capability from the bounding set: %m\n");
+ goto ERROR;
+ }
+ }
+
+ r = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR);
+ if (r) {
+ ERROR(ctx, "Could not set capability %d: %m\n", (int)cap);
+ goto ERROR;
+ }
+
+ r = cap_set_flag(caps, CAP_PERMITTED, 1, &cap, flag);
+ if (r) {
+ ERROR(ctx, "Could not set capability %d: %m\n", (int)cap);
+ goto ERROR;
+ }
+
+ r = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag);
+ if (r) {
+ ERROR(ctx, "Could not set capability %d: %m\n", (int)cap);
+ goto ERROR;
+ }
+ }
+
+ // Restore capabilities
+ r = cap_set_proc(caps);
+ if (r) {
+ ERROR(ctx, "Could not restore capabilities: %m\n");
+ goto ERROR;
+ }
+
+ERROR:
+ if (caps)
+ cap_free(caps);
+
+ return r;
+}
+
+int td_priv_drop_privileges(td_ctx* ctx, const char* user) {
+ struct passwd* passwd = NULL;
+ int r;
+
+ // Fetch the current user
+ uid_t current_uid = getuid();
+
+ // If we have not been launched by root, we will assume that we have already
+ // been launched with a minimal set of privileges.
+ if (current_uid > 0)
+ return 0;
+
+ // Log action
+ DEBUG(ctx, "Dropping privileges...\n");
+
+ // Fetch information about the desired user
+ passwd = getpwnam(user);
+ if (!passwd) {
+ ERROR(ctx, "Could not find user %s: %m\n", user);
+ return -errno;
+ }
+
+ uid_t uid = passwd->pw_uid;
+ gid_t gid = passwd->pw_gid;
+
+ DEBUG(ctx, "Changing to user '%s', uid = %ld, gid = %ld\n", user, uid, gid);
+
+ // Change group
+ r = setresgid(gid, gid, gid);
+ if (r) {
+ ERROR(ctx, "Could not change group to %d: %m\n", gid);
+ return -errno;
+ }
+
+ // Set any supplementary groups
+ r = setgroups(0, NULL);
+ if (r) {
+ ERROR(ctx, "Could not set supplementary groups: %m\n");
+ return -errno;
+ }
+
+ // Do not drop any capabilities when we change to the new user
+ r = prctl(PR_SET_KEEPCAPS, 1);
+ if (r) {
+ ERROR(ctx, "Could not set PR_SET_KEEPCAPS: %m\n");
+ return -errno;
+ }
+
+ // Change to the new user
+ r = setresuid(uid, uid, uid);
+ if (r) {
+ ERROR(ctx, "Could not change user to %d: %m\n", uid);
+ return -errno;
+ }
+
+ // Reset PR_SET_KEEPCAPS
+ r = prctl(PR_SET_KEEPCAPS, 0);
+ if (r) {
+ ERROR(ctx, "Could not set PR_SET_KEEPCAPS: %m\n");
+ return -errno;
+ }
+
+ // Drop capabilities
+ r = td_priv_drop_capabilities(ctx);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
--- /dev/null
+/*#############################################################################
+# #
+# telemetryd - The IPFire Telemetry Collection Service #
+# Copyright (C) 2026 IPFire Development Team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#ifndef TELEMETRY_PRIV_H
+#define TELEMETRY_PRIV_H
+
+#include "ctx.h"
+
+int td_priv_drop_privileges(td_ctx* ctx, const char* user);
+
+#endif /* TELEMETRY_PRIV_H */