]> git.gsnw.org Git - fping.git/commitdiff
fping: retain privileges until after privileged setsockopt
authorHenrique de Moraes Holschuh <henrique@nic.br>
Mon, 24 Aug 2020 23:00:53 +0000 (20:00 -0300)
committerHenrique de Moraes Holschuh <henrique@nic.br>
Tue, 25 Aug 2020 15:53:28 +0000 (12:53 -0300)
On Linux, one needs privileges to setsockopt(SO_BINDTODEVICE), and the
current code would drop setuid root privileges too soon.

Temporarily drop privileges instead, and raise them back to issue the
privileged setsockopt().  Once we know we won't need to do any further
privileged setsockopt(), permanently drop privileges.

For now, assume SO_BINDTODEVICE is the only setsockopt that needs this.

src/fping.c

index 0bc2d81d6bfcedbafce9c332b4da8d020ef7916c..06bf7ddd64980d65a289b850e21d6a783e05dd72 100644 (file)
@@ -395,6 +395,42 @@ struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
 void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
 void update_current_time();
 
+/************************************************************
+
+  Function: p_setsockopt
+
+*************************************************************
+
+  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
+
+  Description:
+
+  Elevates privileges to p_uid when required, calls
+  setsockopt, and drops privileges back.
+
+************************************************************/
+
+int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
+       const void *optval, socklen_t optlen)
+{
+    const uid_t saved_uid = geteuid();
+    int res;
+
+    if (p_uid != saved_uid && seteuid(p_uid)) {
+       perror("cannot elevate privileges for setsockopt");
+    }
+
+    res = setsockopt(sockfd, level, optname, optval, optlen);
+
+    if (p_uid != saved_uid && seteuid(saved_uid)) {
+       perror("fatal error: could not drop privileges after setsockopt");
+       /* continuing would be a security hole */
+       exit(4);
+    }
+
+    return res;
+}
+
 /************************************************************
 
   Function: main
@@ -412,7 +448,7 @@ void update_current_time();
 int main(int argc, char** argv)
 {
     int c;
-    uid_t uid;
+    const uid_t suid = geteuid();
     int tos = 0;
     struct optparse optparse_state;
 #ifdef USE_SIGACTION
@@ -448,9 +484,9 @@ int main(int argc, char** argv)
     memset(&src_addr6, 0, sizeof(src_addr6));
 #endif
 
-    if ((uid = getuid())) {
-        /* drop privileges */
-        if (setuid(getuid()) == -1)
+    if (!suid && suid != getuid()) {
+        /* *temporarily* drop privileges */
+        if (seteuid(getuid()) == -1)
             perror("cannot setuid");
     }
 
@@ -740,14 +776,14 @@ int main(int argc, char** argv)
         case 'I':
 #ifdef SO_BINDTODEVICE
             if (socket4 >= 0) {
-                if (setsockopt(socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
+                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
                     perror("binding to specific interface (SO_BINTODEVICE)");
                     exit(1);
                 }
             }
 #ifdef IPV6
             if (socket6 >= 0) {
-                if (setsockopt(socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
+                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
                     perror("binding to specific interface (SO_BINTODEVICE), IPV6");
                     exit(1);
                 }
@@ -796,6 +832,13 @@ int main(int argc, char** argv)
         }
     }
 
+    /* permanetly drop privileges */
+    if (suid != getuid() && setuid(getuid())) {
+       perror("fatal: failed to permanently drop privileges");
+       /* continuing would be a security hole */
+       exit(4);
+    }
+
     /* validate various option settings */
 
 #ifndef IPV6