diff --git a/src/fping.c b/src/fping.c index 0bc2d81d..06bf7ddd 100644 --- a/src/fping.c +++ b/src/fping.c @@ -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