fping: retain privileges until after privileged setsockopt

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.
pull/200/head
Henrique de Moraes Holschuh 4 years ago
parent 296de3d7a2
commit 59d83ed402

@ -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 stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
void update_current_time(); 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 Function: main
@ -412,7 +448,7 @@ void update_current_time();
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
int c; int c;
uid_t uid; const uid_t suid = geteuid();
int tos = 0; int tos = 0;
struct optparse optparse_state; struct optparse optparse_state;
#ifdef USE_SIGACTION #ifdef USE_SIGACTION
@ -448,9 +484,9 @@ int main(int argc, char** argv)
memset(&src_addr6, 0, sizeof(src_addr6)); memset(&src_addr6, 0, sizeof(src_addr6));
#endif #endif
if ((uid = getuid())) { if (!suid && suid != getuid()) {
/* drop privileges */ /* *temporarily* drop privileges */
if (setuid(getuid()) == -1) if (seteuid(getuid()) == -1)
perror("cannot setuid"); perror("cannot setuid");
} }
@ -740,14 +776,14 @@ int main(int argc, char** argv)
case 'I': case 'I':
#ifdef SO_BINDTODEVICE #ifdef SO_BINDTODEVICE
if (socket4 >= 0) { 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)"); perror("binding to specific interface (SO_BINTODEVICE)");
exit(1); exit(1);
} }
} }
#ifdef IPV6 #ifdef IPV6
if (socket6 >= 0) { 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"); perror("binding to specific interface (SO_BINTODEVICE), IPV6");
exit(1); 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 */ /* validate various option settings */
#ifndef IPV6 #ifndef IPV6

Loading…
Cancel
Save