]> git.ipfire.org Git - thirdparty/mtr.git/commitdiff
Fix Capability Management, Retain CAP_NET_ADMIN
authorflu0r1ne <flu0r1ne@flu0r1ne.net>
Fri, 29 Sep 2023 08:07:54 +0000 (03:07 -0500)
committerflu0r1ne <flu0r1ne@flu0r1ne.net>
Fri, 29 Sep 2023 23:36:37 +0000 (18:36 -0500)
Modify the capability-dropping logic to specifically retain CAP_NET_ADMIN if it
is initially provided, in adherence to least-privilege principles.

Details:
1. Update the `drop_excess_capabilities` function to only drop capabilities that
   are unnecessary, retaining CAP_NET_ADMIN when needed for setting the socket
   mark.
2. Introduce logic in `set_socket_mark` to temporarily elevate CAP_NET_ADMIN
   into the effective set for the duration of the packet mark setting
   operation.

packet/construct_unix.c
packet/packet.c

index e09d70576471c279c190e0f4fb8c7e5ac655fd6d..a9d6d91fdb23da9e3deb55c0e8d2c4f0b820f149 100644 (file)
 #define SOL_IP IPPROTO_IP
 #endif
 
+#ifdef HAVE_LIBCAP
+#include <sys/capability.h>
+#endif
+
 /*  A source of data for computing a checksum  */
 struct checksum_source_t {
     const void *data;
@@ -278,6 +282,67 @@ int construct_udp6_packet(
     return 0;
 }
 
+#define SET_MARK_N_ADDED_CAPS 1
+
+/* Set the socket mark */
+static int set_socket_mark(int socket, unsigned int mark) {
+    int result = -1;
+
+    // Add CAP_NET_ADMIN to the effective set if libcap is present
+#ifdef HAVE_LIBCAP
+    cap_t cap = NULL;
+    static cap_value_t cap_add[SET_MARK_N_ADDED_CAPS] = { CAP_NET_ADMIN };
+
+    // Get the capabilities of the current process
+    cap = cap_get_proc();
+    if (cap == NULL) {
+        goto cleanup_and_exit;
+    }
+
+    // Set the required capability flag
+    if (cap_set_flag(cap, CAP_EFFECTIVE, SET_MARK_N_ADDED_CAPS, cap_add,
+        CAP_SET)) {
+        goto cleanup_and_exit;
+    }
+
+    // Apply the modified capabilities to the current process
+    if (cap_set_proc(cap)) {
+        goto cleanup_and_exit;
+    }
+#endif /* ifdef HAVE_LIBPCAP */
+
+    // Set the socket mark
+    if (setsockopt(socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark))) {
+        goto cleanup_and_exit;
+    }
+
+    // Drop CAP_NET_ADMIN from the effective set if libcap is present
+#ifdef HAVE_LIBCAP
+
+    // Clear the CAP_NET_ADMIN capability flag
+    if (cap_set_flag(cap, CAP_EFFECTIVE, SET_MARK_N_ADDED_CAPS, cap_add,
+        CAP_CLEAR)) {
+        goto cleanup_and_exit;
+    }
+
+    // Apply the modified capabilities to the current process
+    if (cap_set_proc(cap)) {
+        goto cleanup_and_exit;
+    }
+#endif /* ifdef HAVE_LIBPCAP */
+
+    result = 0; // Success
+
+cleanup_and_exit:
+
+#ifdef HAVE_LIBCAP
+    cap_free(cap);
+#endif /* ifdef HAVE_LIBPCAP */
+
+    return result;
+}
+
+
 /*
     Set the socket options for an outgoing stream protocol socket based on
     the packet parameters.
@@ -341,8 +406,7 @@ int set_stream_socket_options(
     }
 #ifdef SO_MARK
     if (param->routing_mark) {
-        if (setsockopt(stream_socket, SOL_SOCKET,
-                       SO_MARK, &param->routing_mark, sizeof(int))) {
+        if (set_socket_mark(stream_socket, param->routing_mark)) {
             return -1;
         }
     }
@@ -360,6 +424,7 @@ int set_stream_socket_options(
     return 0;
 }
 
+
 /*
     Open a TCP or SCTP socket, respecting the probe paramters as much as
     we can, and use it as an outgoing probe.
@@ -577,8 +642,7 @@ int construct_ip4_packet(
      */
 #ifdef SO_MARK
     if (param->routing_mark) {
-        if (setsockopt(send_socket, SOL_SOCKET,
-                       SO_MARK, &param->routing_mark, sizeof(int))) {
+        if (set_socket_mark(send_socket, param->routing_mark)) {
             return -1;
         }
     }
@@ -750,9 +814,7 @@ int construct_ip6_packet(
     }
 #ifdef SO_MARK
     if (param->routing_mark) {
-        if (setsockopt(send_socket,
-                       SOL_SOCKET, SO_MARK, &param->routing_mark,
-                       sizeof(int))) {
+        if (set_socket_mark(send_socket, param->routing_mark)) {
             return -1;
         }
     }
index 3821d91bda8e3ea190f5c37e9c76d1691cf8a9dd..ba37d25ce3e1d8784e61a4f4a37b6e8e8fbea485 100644 (file)
 
 #include "wait.h"
 
+#define N_ENTRIES(array) \
+    (sizeof((array)) / sizeof(*(array)))
+
+#ifdef HAVE_LIBCAP
+static
+void drop_excess_capabilities() {
+    cap_value_t cap_permitted[] = {
+#ifdef SO_MARK
+    /*
+      By default, the root user has all capabilities, which poses a security risk.
+      Since the socket has already been opened, we only need CAP_NET_ADMIN to set
+      the fwmark. This capability must remain in the permitted set so that it can
+      be added to the effective set when needed.
+    */
+        CAP_NET_ADMIN
+#endif /* ifdef SOMARK */
+    };
+
+    cap_t current_cap = cap_get_proc();
+    cap_t wanted_cap = cap_get_proc();
+
+    if(!current_cap || !wanted_cap) {
+        goto pcap_error;
+    }
+
+    // Clear all capabilities from the 'wanted_cap' set
+    if(cap_clear(wanted_cap)) {
+        goto pcap_error;
+    }
+
+    // Retain only the necessary capabilities defined in 'cap_permitted' in the permitted set.
+    // This approach ensures the principle of least privilege.
+    // If the user has dropped capabilities, the code assumes those features will not be needed.
+    for(unsigned i = 0; i < N_ENTRIES(cap_permitted); i++) {
+        cap_flag_value_t is_set;
+
+        if(cap_get_flag(current_cap, cap_permitted[i], CAP_PERMITTED, &is_set)) {
+            goto pcap_error;
+        }
+
+        if(cap_set_flag(wanted_cap, CAP_PERMITTED, 1, &cap_permitted[i], is_set)) {
+            goto pcap_error;
+        }
+    }
+
+    // Update the process's capabilities to match 'wanted_cap'
+    if(cap_set_proc(wanted_cap)) {
+        goto pcap_error;
+    }
+
+    if(cap_free(current_cap) || cap_free(wanted_cap)) {
+        goto pcap_error;
+    }
+
+    return;
+
+pcap_error:
+
+    cap_free(current_cap);
+    cap_free(wanted_cap);
+    error(EXIT_FAILURE, errno, "Failed to drop capabilities");
+}
+#endif /* ifdef HAVE_LIBCAP */
+
 /*  Drop SUID privileges.  To be used after acquiring raw sockets.  */
 static
 int drop_elevated_permissions(
     void)
 {
-#ifdef HAVE_LIBCAP
-    cap_t cap;
-#endif
-
     /*  Drop any suid permissions granted  */
     if (setgid(getgid()) || setuid(getuid())) {
         return -1;
@@ -55,19 +115,9 @@ int drop_elevated_permissions(
 
     /*
        Drop all process capabilities.
-       This will revoke anything granted by a commandline 'setcap'
      */
 #ifdef HAVE_LIBCAP
-    cap = cap_get_proc();
-    if (cap == NULL) {
-        return -1;
-    }
-    if (cap_clear(cap)) {
-        return -1;
-    }
-    if (cap_set_proc(cap)) {
-        return -1;
-    }
+    drop_excess_capabilities();
 #endif
 
     return 0;