]> git.ipfire.org Git - people/ms/network.git/commitdiff
networkd: Drop all capabilities except a few we would like to keep
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 5 Feb 2023 13:17:21 +0000 (13:17 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 5 Feb 2023 13:17:21 +0000 (13:17 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
configure.ac
src/networkd/main.c

index 2ebe622410422e1f1fc367ac0126898482c20b05..df615527bf21a9b21bfb63da8e05d9742475e9ec 100644 (file)
@@ -334,12 +334,14 @@ networkd_CPPFLAGS = \
 
 networkd_CFLAGS = \
        $(AM_CFLAGS) \
+       $(CAP_CFLAGS) \
        $(SYSTEMD_CFLAGS)
 
 networkd_LDFLAGS = \
        $(AM_LDFLAGS)
 
 networkd_LDADD = \
+       $(CAP_LIBS) \
        $(SYSTEMD_LIBS)
 
 dist_dbuspolicy_DATA += \
index 16f0724f108532a121f676c1c6d5c2d0b42a9453..e1baa64bdfb31c2198e65389db7f959487093ae0 100644 (file)
@@ -172,6 +172,7 @@ AM_CONDITIONAL(HAVE_UDEV, [test -n "$with_udevdir"])
 
 # ------------------------------------------------------------------------------
 
+PKG_CHECK_MODULES([CAP], [libcap])
 PKG_CHECK_MODULES([LIBNL], [libnl-3.0 libnl-genl-3.0])
 PKG_CHECK_MODULES([SYSTEMD], [libsystemd])
 
index fbd6c3b9938a070c9714eb3579db4d72a74244a1..9a0fd74d744238b074d558fb58ccbb3b16461834 100644 (file)
 #############################################################################*/
 
 #include <grp.h>
+#include <linux/capability.h>
 #include <pwd.h>
 #include <stddef.h>
+#include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include "daemon.h"
 #include "logging.h"
 
+static int cap_acquire_setpcap(void) {
+       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("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("Could not set CAP_SETPCAP: %m\n");
+                       goto ERROR;
+               }
+
+               // Store capabilities
+               r = cap_set_proc(caps);
+               if (r) {
+                       ERROR("Could not acquire effective CAP_SETPCAP capability: %m\n");
+                       goto ERROR;
+               }
+       }
+
+ERROR:
+       if (caps)
+               cap_free(caps);
+
+       return r;
+}
+
+static cap_flag_value_t 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 drop_capabilities(void) {
+       int r;
+
+       const cap_value_t keep_caps[] = {
+               CAP_NET_ADMIN,
+               CAP_NET_BIND_SERVICE,
+               CAP_NET_BROADCAST,
+               CAP_NET_RAW,
+               0,
+       };
+
+       // Acquire CAP_SETPCAP
+       r = cap_acquire_setpcap();
+       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 = 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("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("Could not set capability %d: %m\n", (int)cap);
+                       goto ERROR;
+               }
+
+               r = cap_set_flag(caps, CAP_PERMITTED, 1, &cap, flag);
+               if (r) {
+                       ERROR("Could not set capability %d: %m\n", (int)cap);
+                       goto ERROR;
+               }
+
+               r = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag);
+               if (r) {
+                       ERROR("Could not set capability %d: %m\n", (int)cap);
+                       goto ERROR;
+               }
+       }
+
+       // Restore capabilities
+       r = cap_set_proc(caps);
+       if (r) {
+               ERROR("Could not restore capabilities: %m\n");
+               goto ERROR;
+       }
+
+ERROR:
+       if (caps)
+               cap_free(caps);
+
+       return r;
+}
+
 static int drop_privileges(const char* user) {
        struct passwd* passwd = NULL;
        int r;
@@ -87,6 +203,11 @@ static int drop_privileges(const char* user) {
                return 1;
        }
 
+       // Drop capabilities
+       r = drop_capabilities();
+       if (r)
+               return r;
+
        return 0;
 }