]> git.ipfire.org Git - telemetry.git/commitdiff
daemon: Drop privileges after start
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 24 Feb 2026 11:37:17 +0000 (11:37 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 24 Feb 2026 11:37:17 +0000 (11:37 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/daemon/main.c
src/daemon/priv.c [new file with mode: 0644]
src/daemon/priv.h [new file with mode: 0644]

index 00ba6ae8c9c03d33bca0b4011ea9954be0f1f234..0f9466c0391d8db22dbbd3126137b93fe572c2a9 100644 (file)
@@ -166,6 +166,8 @@ dist_telemetryd_SOURCES = \
        src/daemon/metrics.h \
        src/daemon/parse.c \
        src/daemon/parse.h \
+       src/daemon/priv.c \
+       src/daemon/priv.h \
        src/daemon/proc.c \
        src/daemon/proc.h \
        src/daemon/proto.c \
index 16129e1edcb9abd80b68d31d627a15091cdf0d80..70270daf5026bf08d1d0536118591da4da0190d6 100644 (file)
@@ -27,6 +27,7 @@
 #include "ctx.h"
 #include "daemon.h"
 #include "logging.h"
+#include "priv.h"
 #include "string.h"
 
 const char* argp_program_version = PACKAGE_VERSION;
@@ -98,6 +99,11 @@ int main(int argc, char* argv[]) {
        if (r)
                goto ERROR;
 
+       // Drop privileges
+       r = td_priv_drop_privileges(ctx, user);
+       if (r < 0)
+               goto ERROR;
+
        // Create a daemon
        r = td_daemon_create(&daemon, ctx, sources);
        if (r < 0) {
diff --git a/src/daemon/priv.c b/src/daemon/priv.c
new file mode 100644 (file)
index 0000000..f033b4e
--- /dev/null
@@ -0,0 +1,214 @@
+/*#############################################################################
+#                                                                             #
+# 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;
+}
diff --git a/src/daemon/priv.h b/src/daemon/priv.h
new file mode 100644 (file)
index 0000000..609fc76
--- /dev/null
@@ -0,0 +1,28 @@
+/*#############################################################################
+#                                                                             #
+# 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 */