linux - Why does CAP_NET_RAW not work with SO_BINDTODEVICE? -


i have following simple test program create udp socket , bind specific interface so_bindtodevice can bind() inaddr_any recieve udp broadcasts on interface.

//filename: bindtest.c #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h>  #define my_port (333) #define my_device "enp0s3"  #define buffersize (1000)  /* global variables */ int sock; struct sockaddr_in sa; struct sockaddr_in my_addr; char buffer[buffersize];  int main(int argc, char *argv[]) {   unsigned int echolen, clientlen;   int rc, n;   char opt_buffer[1000];   struct protoent *udp_protoent;   struct timeval receive_timeout;   int optval;   socklen_t opt_length;   sleep(1);   /* create udp socket */   if ((sock = socket(af_inet, sock_dgram, ipproto_udp)) < 0)   {     printf ("%s: failed create udp socket (%s) \n",         argv[0], strerror(errno));     exit (exit_failure);   }   printf ("udp socket created\n");    /* set recvfrom timeout value */   receive_timeout.tv_sec = 5;   receive_timeout.tv_usec = 0;   rc=setsockopt(sock, sol_socket, so_rcvtimeo, &receive_timeout, sizeof(receive_timeout));   if (rc != 0)   {      printf ("%s: not set so_rcvtimeo (%s)\n",         argv[0], strerror(errno));      exit (exit_failure);   }   printf ("set timeout time [s]: %d time [ms]: %d\n", receive_timeout.tv_sec, receive_timeout.tv_usec);   /* allow broadcast messages socket */   int true = 1;   rc=setsockopt(sock, sol_socket, so_broadcast, &true, sizeof(true));   if (rc != 0)   {      printf ("%s: not set so_broadcast (%s)\n",         argv[0], strerror(errno));      exit (exit_failure);   }   printf ("set so_broadcast worked\n");   /* bind specific interface */   char device[] = my_device;   rc=setsockopt(sock, sol_socket, so_bindtodevice, device, sizeof(device));   if (rc != 0)   {      printf ("%s: not set so_bindtodevice (%s)\n",         argv[0], strerror(errno));      exit (exit_failure);   }   printf ("so_bindtodevice worked\n");    /* bind own port */   my_addr.sin_family = af_inet;   my_addr.sin_addr.s_addr = inaddr_any;   my_addr.sin_port = htons(my_port);   rc = bind (sock, (struct sockaddr *) &my_addr, sizeof(my_addr));   if (rc < 0)   {      printf ("%s: not bind port (%s)\n",         argv[0], strerror(errno));      exit (exit_failure);   }   printf ("bind() worked\n");   sa.sin_family = af_inet;   sa.sin_addr.s_addr = inaddr_broadcast;   sa.sin_port = htons(my_port);    char data[20];   sprintf(data,"foobar");   int res = sendto(sock, &data, strlen(data), 0, (struct sockaddr*)&sa, sizeof(sa));   if(res < 0){     printf("could not send\n");   } else {     printf("data sent\n");   }     close(sock);   printf ("socket closed\n");    exit(0); } 

when run program non-root user following output:

$ ./bindtest  udp socket created set timeout time [s]: 5 time [ms]: 0 set so_broadcast worked ./bindtest: not set so_bindtodevice (operation not permitted) 

which pretty logical because i'm not root , so_bindtodevice priviledged operation. included in capability cap_net_raw understand this snippet of code linux kernel:

static int sock_setbindtodevice(struct sock *sk, char __user *optval,                                 int optlen)  {          int ret = -enoprotoopt;  #ifdef config_netdevices          struct net *net = sock_net(sk);          char devname[ifnamsiz];          int index;           /* sorry... */          ret = -eperm;          if (!ns_capable(net->user_ns, cap_net_raw))                  goto out; 

well still when do:

$ getcap bindtest $ sudo setcap cap_net_raw+ep bindtest $ getcap bindtest bindtest = cap_net_raw+ep 

i same error output:

$ ./bindtest  udp socket created set timeout time [s]: 5 time [ms]: 0 set so_broadcast worked ./bindtest: not set so_bindtodevice (operation not permitted) 

and of course works root:

$ sudo ./bindtest udp socket created set timeout time [s]: 5 time [ms]: 0 set so_broadcast worked so_bindtodevice worked bind() worked data sent socket closed 

so why don't work expected?

the code correct, usage of getcap/setcap correct, else must blocking working.

and indeed because of done in /home/user on system mounted nosuid option among others.

so moving binary e.g. /usr/bin/ or other part not mounted nosuid work expected in first place.

(although need cap_net_bind_service bind() work port 333 in example)


Comments

Popular posts from this blog

sublimetext3 - what keyboard shortcut is to comment/uncomment for this script tag in sublime -

java - No use of nillable="0" in SOAP Webservice -

ubuntu - Laravel 5.2 quickstart guide gives Not Found Error -