Home | History | Annotate | Download | only in lease-access
      1 Index: src/dnsmasq.c
      2 ===================================================================
      3 --- src/dnsmasq.c	(revision 696)
      4 +++ src/dnsmasq.c	(revision 821)
      5 @@ -59,7 +59,6 @@
      6  static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
      7  static void check_dns_listeners(fd_set *set, time_t now);
      8  static void sig_handler(int sig);
      9 -static void async_event(int pipe, time_t now);
     10  static void fatal_event(struct event_desc *ev);
     11  static void poll_resolv(void);
     12  
     13 @@ -275,7 +274,7 @@
     14    piperead = pipefd[0];
     15    pipewrite = pipefd[1];
     16    /* prime the pipe to load stuff first time. */
     17 -  send_event(pipewrite, EVENT_RELOAD, 0); 
     18 +  send_event(pipewrite, EVENT_RELOAD, 0, 0); 
     19  
     20    err_pipe[1] = -1;
     21    
     22 @@ -340,7 +339,7 @@
     23  	    }
     24  	  else if (getuid() == 0)
     25  	    {
     26 -	      send_event(err_pipe[1], EVENT_PIDFILE, errno);
     27 +	      send_event(err_pipe[1], EVENT_PIDFILE, errno, 0);
     28  	      _exit(0);
     29  	    }
     30  	}
     31 @@ -372,7 +371,7 @@
     32  	  (setgroups(0, &dummy) == -1 ||
     33  	   setgid(gp->gr_gid) == -1))
     34  	{
     35 -	  send_event(err_pipe[1], EVENT_GROUP_ERR, errno);
     36 +	  send_event(err_pipe[1], EVENT_GROUP_ERR, errno, 0);
     37  	  _exit(0);
     38  	}
     39    
     40 @@ -415,14 +414,14 @@
     41  
     42  	  if (bad_capabilities != 0)
     43  	    {
     44 -	      send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities);
     45 +	      send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities, 0);
     46  	      _exit(0);
     47  	    }
     48  	  
     49  	  /* finally drop root */
     50  	  if (setuid(ent_pw->pw_uid) == -1)
     51  	    {
     52 -	      send_event(err_pipe[1], EVENT_USER_ERR, errno);
     53 +	      send_event(err_pipe[1], EVENT_USER_ERR, errno, 0);
     54  	      _exit(0);
     55  	    }     
     56  
     57 @@ -434,7 +433,7 @@
     58  	  /* lose the setuid and setgid capbilities */
     59  	  if (capset(hdr, data) == -1)
     60  	    {
     61 -	      send_event(err_pipe[1], EVENT_CAP_ERR, errno);
     62 +	      send_event(err_pipe[1], EVENT_CAP_ERR, errno, 0);
     63  	      _exit(0);
     64  	    }
     65  #endif
     66 @@ -647,7 +646,7 @@
     67  	}
     68        
     69        if (FD_ISSET(piperead, &rset))
     70 -	async_event(piperead, now);
     71 +	async_event(piperead, now, NULL, 0);
     72        
     73  #ifdef HAVE_LINUX_NETWORK
     74        if (FD_ISSET(daemon->netlinkfd, &rset))
     75 @@ -674,7 +673,7 @@
     76  #endif      
     77  
     78        if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
     79 -	dhcp_packet(now);
     80 +	dhcp_packet(piperead, now);
     81  
     82  #ifndef NO_FORK
     83        if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
     84 @@ -719,17 +718,18 @@
     85        else
     86  	return;
     87  
     88 -      send_event(pipewrite, event, 0); 
     89 +      send_event(pipewrite, event, 0, 0); 
     90        errno = errsave;
     91      }
     92  }
     93  
     94 -void send_event(int fd, int event, int data)
     95 +void send_event(int fd, int event, int data, int priv)
     96  {
     97    struct event_desc ev;
     98    
     99    ev.event = event;
    100    ev.data = data;
    101 +  ev.priv = priv;
    102    
    103    /* error pipe, debug mode. */
    104    if (fd == -1)
    105 @@ -771,14 +771,17 @@
    106        die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
    107      }
    108  }	
    109 -      
    110 -static void async_event(int pipe, time_t now)
    111 +
    112 +/* returns the private data of the event
    113 + */
    114 +int async_event(int pipe, time_t now, struct event_desc* event, unsigned int secs)
    115  {
    116    pid_t p;
    117    struct event_desc ev;
    118    int i;
    119  
    120 -  if (read_write(pipe, (unsigned char *)&ev, sizeof(ev), 1))
    121 +  if (read_timeout(pipe, (unsigned char *)&ev, sizeof(ev), now, secs) > 0) 
    122 +    {
    123      switch (ev.event)
    124        {
    125        case EVENT_RELOAD:
    126 @@ -872,6 +875,14 @@
    127  	flush_log();
    128  	exit(EC_GOOD);
    129        }
    130 +    }
    131 +  else
    132 +    return -1; /* timeout */
    133 +
    134 +  if (event)
    135 +    memcpy( event, &ev, sizeof(ev));
    136 +    
    137 +  return 0;
    138  }
    139  
    140  static void poll_resolv()
    141 Index: src/config.h
    142 ===================================================================
    143 --- src/config.h	(revision 696)
    144 +++ src/config.h	(revision 821)
    145 @@ -51,6 +51,8 @@
    146  #define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
    147  #define LOG_MAX 5 /* log-queue length */
    148  #define RANDFILE "/dev/urandom"
    149 +#define SCRIPT_TIMEOUT 6
    150 +#define LEASE_CHECK_TIMEOUT 10
    151  
    152  /* DBUS interface specifics */
    153  #define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
    154 Index: src/dnsmasq.h
    155 ===================================================================
    156 --- src/dnsmasq.h	(revision 696)
    157 +++ src/dnsmasq.h	(revision 821)
    158 @@ -116,6 +116,7 @@
    159  /* Async event queue */
    160  struct event_desc {
    161    int event, data;
    162 +  unsigned int priv;
    163  };
    164  
    165  #define EVENT_RELOAD    1
    166 @@ -390,6 +391,7 @@
    167  #define ACTION_OLD_HOSTNAME  2
    168  #define ACTION_OLD           3
    169  #define ACTION_ADD           4
    170 +#define ACTION_ACCESS        5
    171  
    172  #define DHCP_CHADDR_MAX 16
    173  
    174 @@ -709,6 +711,7 @@
    175  char *print_mac(char *buff, unsigned char *mac, int len);
    176  void bump_maxfd(int fd, int *max);
    177  int read_write(int fd, unsigned char *packet, int size, int rw);
    178 +int read_timeout(int fd, unsigned char *packet, int size, time_t now, int secs);
    179  
    180  /* log.c */
    181  void die(char *message, char *arg1, int exit_code);
    182 @@ -748,7 +751,7 @@
    183  
    184  /* dhcp.c */
    185  void dhcp_init(void);
    186 -void dhcp_packet(time_t now);
    187 +void dhcp_packet(int piperead, time_t now);
    188  
    189  struct dhcp_context *address_available(struct dhcp_context *context, 
    190  				       struct in_addr addr,
    191 @@ -792,14 +795,16 @@
    192  void rerun_scripts(void);
    193  
    194  /* rfc2131.c */
    195 -size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
    196 +size_t dhcp_reply(int pipefd, struct dhcp_context *context, char *iface_name, int int_index,
    197  		  size_t sz, time_t now, int unicast_dest, int *is_inform);
    198  
    199  /* dnsmasq.c */
    200  int make_icmp_sock(void);
    201  int icmp_ping(struct in_addr addr);
    202 -void send_event(int fd, int event, int data);
    203 +void send_event(int fd, int event, int data, int priv);
    204  void clear_cache_and_reload(time_t now);
    205 +int wait_for_child(int pipe);
    206 +int async_event(int pipe, time_t now, struct event_desc*, unsigned int timeout);
    207  
    208  /* isc.c */
    209  #ifdef HAVE_ISC_READER
    210 @@ -832,9 +837,9 @@
    211  /* helper.c */
    212  #ifndef NO_FORK
    213  int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
    214 -void helper_write(void);
    215 +int helper_write(void);
    216  void queue_script(int action, struct dhcp_lease *lease, 
    217 -		  char *hostname, time_t now);
    218 +		  char *hostname, time_t now, unsigned int uid);
    219  int helper_buf_empty(void);
    220  #endif
    221  
    222 Index: src/util.c
    223 ===================================================================
    224 --- src/util.c	(revision 696)
    225 +++ src/util.c	(revision 821)
    226 @@ -444,3 +444,38 @@
    227    return 1;
    228  }
    229  
    230 +int read_timeout(int fd, unsigned char *packet, int size, time_t now, int secs)
    231 +{
    232 +  ssize_t n, done;
    233 +  time_t expire;
    234 +  
    235 +  expire = now + secs;
    236 +  
    237 +  for (done = 0; done < size; done += n)
    238 +    {
    239 +    retry:
    240 +      if (secs > 0) alarm(secs);
    241 +      n = read(fd, &packet[done], (size_t)(size - done));
    242 +
    243 +      if (n == 0)
    244 +        return 0;
    245 +      else if (n == -1)
    246 +        {
    247 +          if (errno == EINTR) {
    248 +            my_syslog(LOG_INFO, _("read timed out (errno %d)"), errno);
    249 +            return 0;
    250 +          }
    251 +
    252 +          if (retry_send() || errno == ENOMEM || errno == ENOBUFS || errno == EAGAIN)
    253 +            {
    254 +              if (secs == 0 || (secs > 0 && dnsmasq_time() < expire))
    255 +                goto retry;
    256 +            }
    257 +
    258 +          my_syslog(LOG_INFO, _("error in read (timeout %d, errno %d)"), secs, errno);
    259 +          return 0;
    260 +        }
    261 +    }
    262 +  return 1;
    263 +}
    264 +
    265 Index: src/dhcp.c
    266 ===================================================================
    267 --- src/dhcp.c	(revision 696)
    268 +++ src/dhcp.c	(revision 821)
    269 @@ -103,7 +103,7 @@
    270    daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
    271  }
    272    
    273 -void dhcp_packet(time_t now)
    274 +void dhcp_packet(int piperead, time_t now)
    275  {
    276    struct dhcp_packet *mess;
    277    struct dhcp_context *context;
    278 @@ -239,7 +239,8 @@
    279    if (!iface_enumerate(&parm, complete_context, NULL))
    280      return;
    281    lease_prune(NULL, now); /* lose any expired leases */
    282 -  iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, 
    283 +
    284 +  iov.iov_len = dhcp_reply(piperead, parm.current, ifr.ifr_name, iface_index, (size_t)sz, 
    285  			   now, unicast_dest, &is_inform);
    286    lease_update_file(now);
    287    lease_update_dns();
    288 Index: src/helper.c
    289 ===================================================================
    290 --- src/helper.c	(revision 696)
    291 +++ src/helper.c	(revision 821)
    292 @@ -45,6 +45,7 @@
    293  #endif
    294    unsigned char hwaddr[DHCP_CHADDR_MAX];
    295    char interface[IF_NAMESIZE];
    296 +  unsigned int uid;
    297  };
    298  
    299  static struct script_data *buf = NULL;
    300 @@ -60,7 +61,7 @@
    301       then fork our process. */
    302    if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
    303      {
    304 -      send_event(err_fd, EVENT_PIPE_ERR, errno);
    305 +      send_event(err_fd, EVENT_PIPE_ERR, errno, 0);
    306        _exit(0);
    307      }
    308  
    309 @@ -87,13 +88,13 @@
    310  	{
    311  	  if (daemon->options & OPT_NO_FORK)
    312  	    /* send error to daemon process if no-fork */
    313 -	    send_event(event_fd, EVENT_HUSER_ERR, errno);
    314 +	    send_event(event_fd, EVENT_HUSER_ERR, errno, 0);
    315  	  else
    316  	    {
    317  	      /* kill daemon */
    318 -	      send_event(event_fd, EVENT_DIE, 0);
    319 +	      send_event(event_fd, EVENT_DIE, 0, 0);
    320  	      /* return error */
    321 -	      send_event(err_fd, EVENT_HUSER_ERR, errno);;
    322 +	      send_event(err_fd, EVENT_HUSER_ERR, errno, 0);
    323  	    }
    324  	  _exit(0);
    325  	}
    326 @@ -122,6 +123,8 @@
    327  	action_str = "del";
    328        else if (data.action == ACTION_ADD)
    329  	action_str = "add";
    330 +      else if (data.action == ACTION_ACCESS)
    331 +	action_str = "access";
    332        else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
    333  	action_str = "old";
    334        else
    335 @@ -178,9 +181,11 @@
    336  		{
    337  		  /* On error send event back to main process for logging */
    338  		  if (WIFSIGNALED(status))
    339 -		    send_event(event_fd, EVENT_KILLED, WTERMSIG(status));
    340 -		  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
    341 -		    send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status));
    342 +		    send_event(event_fd, EVENT_KILLED, WTERMSIG(status), data.uid);
    343 +		  else if (WIFEXITED(status))
    344 +		    send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), data.uid);
    345 +                  else
    346 +		    send_event(event_fd, EVENT_EXITED, -1, data.uid);
    347  		  break;
    348  		}
    349  	      
    350 @@ -263,7 +268,7 @@
    351  	  err = errno;
    352  	}
    353        /* failed, send event so the main process logs the problem */
    354 -      send_event(event_fd, EVENT_EXEC_ERR, err);
    355 +      send_event(event_fd, EVENT_EXEC_ERR, err, data.uid);
    356        _exit(0); 
    357      }
    358  }
    359 @@ -295,7 +300,7 @@
    360  }
    361   
    362  /* pack up lease data into a buffer */    
    363 -void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
    364 +void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now, unsigned int uid)
    365  {
    366    unsigned char *p;
    367    size_t size;
    368 @@ -332,6 +337,7 @@
    369        buf_size = size;
    370      }
    371  
    372 +  buf->uid = uid;
    373    buf->action = action;
    374    buf->hwaddr_len = lease->hwaddr_len;
    375    buf->hwaddr_type = lease->hwaddr_type;
    376 @@ -393,12 +399,15 @@
    377    return bytes_in_buf == 0;
    378  }
    379  
    380 -void helper_write(void)
    381 +/* returns -1 if write failed for a reason, 1 if no data exist
    382 + * and 0 if everything was ok.
    383 + */
    384 +int helper_write(void)
    385  {
    386    ssize_t rc;
    387  
    388    if (bytes_in_buf == 0)
    389 -    return;
    390 +    return 1;
    391    
    392    if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
    393      {
    394 @@ -409,9 +418,11 @@
    395    else
    396      {
    397        if (errno == EAGAIN || errno == EINTR)
    398 -	return;
    399 +	return -1;
    400        bytes_in_buf = 0;
    401      }
    402 +    
    403 +  return 0;
    404  }
    405  
    406  #endif
    407 Index: src/rfc2131.c
    408 ===================================================================
    409 --- src/rfc2131.c	(revision 696)
    410 +++ src/rfc2131.c	(revision 821)
    411 @@ -100,8 +100,49 @@
    412  				      int clid_len, unsigned char *clid, int *len_out);
    413  static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); 
    414  
    415 +static int check_access_script( int piperead, struct dhcp_lease *lease, struct dhcp_packet *mess, time_t now)
    416 +{
    417 +#ifndef NO_FORK
    418 +unsigned int uid;
    419 +struct event_desc ev;
    420 +int ret;
    421 +struct dhcp_lease _lease;
    422 +
    423 +  if (daemon->lease_change_command == NULL) return 0; /* ok */
    424 +
    425 +  if (!lease) { /* if host has not been seen before lease is NULL */
    426 +      memset(&_lease, 0, sizeof(_lease));
    427 +      lease = &_lease;
    428 +      lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
    429 +  }
    430 +
    431 +  uid = rand16();
    432 +  queue_script(ACTION_ACCESS, lease, NULL, now, uid);
    433 +
    434 +  /* send all data to helper process */
    435 +  do 
    436 +    {
    437 +      helper_write();
    438 +    } while (helper_buf_empty() == 0);
    439 +
    440 +  /* wait for our event */
    441 +  ret = 0;
    442 +  do 
    443 +    {
    444 +      ret = async_event( piperead, now, &ev, SCRIPT_TIMEOUT);
    445 +    }
    446 +  while(ev.priv != uid && ret >= 0);
    447 +
    448 +  if (ret < 0 || ev.data != 0) /* timeout or error */
    449 +    {
    450 +      return -1;
    451 +    }
    452 +
    453 +#endif
    454 +  return 0; /* ok */
    455 +}
    456  	  
    457 -size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
    458 +size_t dhcp_reply(int piperead, struct dhcp_context *context, char *iface_name, int int_index,
    459  		  size_t sz, time_t now, int unicast_dest, int *is_inform)
    460  {
    461    unsigned char *opt, *clid = NULL;
    462 @@ -252,7 +293,7 @@
    463  	mac->netid.next = netid;
    464  	netid = &mac->netid;
    465        }
    466 -  
    467 +
    468    /* Determine network for this packet. Our caller will have already linked all the 
    469       contexts which match the addresses of the receiving interface but if the 
    470       machine has an address already, or came via a relay, or we have a subnet selector, 
    471 @@ -329,7 +370,7 @@
    472  	    my_syslog(LOG_INFO, _("Available DHCP range: %s -- %s"), daemon->namebuff, inet_ntoa(context_tmp->end));
    473  	}
    474      }
    475 -
    476 +    
    477    mess->op = BOOTREPLY;
    478    
    479    config = find_config(daemon->dhcp_conf, context, clid, clid_len, 
    480 @@ -418,7 +459,7 @@
    481  	      else
    482  		mess->yiaddr = lease->addr;
    483  	    }
    484 -	  
    485 +
    486  	  if (!message && 
    487  	      !lease && 
    488  	      (!(lease = lease_allocate(mess->yiaddr))))
    489 @@ -641,7 +682,14 @@
    490        memcpy(req_options, option_ptr(opt, 0), option_len(opt));
    491        req_options[option_len(opt)] = OPTION_END;
    492      }
    493 -  
    494 +
    495 +  if (mess_type == DHCPREQUEST || mess_type == DHCPDISCOVER)
    496 +    if (check_access_script(piperead, lease, mess, now) < 0)
    497 +      {
    498 +        my_syslog(LOG_INFO, _("Ignoring client due to access script"));
    499 +        return 0;
    500 +      }
    501 +
    502    switch (mess_type)
    503      {
    504      case DHCPDECLINE:
    505 Index: src/log.c
    506 ===================================================================
    507 --- src/log.c	(revision 696)
    508 +++ src/log.c	(revision 821)
    509 @@ -73,7 +73,7 @@
    510  
    511    if (!log_reopen(daemon->log_file))
    512      {
    513 -      send_event(errfd, EVENT_LOG_ERR, errno);
    514 +      send_event(errfd, EVENT_LOG_ERR, errno, 0);
    515        _exit(0);
    516      }
    517  
    518 Index: src/lease.c
    519 ===================================================================
    520 --- src/lease.c	(revision 696)
    521 +++ src/lease.c	(revision 821)
    522 @@ -511,7 +511,7 @@
    523        if (lease->old_hostname)
    524  	{
    525  #ifndef NO_FORK
    526 -	  queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
    527 +	  queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now, 0);
    528  #endif
    529  	  free(lease->old_hostname);
    530  	  lease->old_hostname = NULL;
    531 @@ -520,7 +520,7 @@
    532        else 
    533  	{
    534  #ifndef NO_FORK
    535 -	  queue_script(ACTION_DEL, lease, lease->hostname, now);
    536 +	  queue_script(ACTION_DEL, lease, lease->hostname, now, 0);
    537  #endif
    538  	  old_leases = lease->next;
    539  	  
    540 @@ -540,7 +540,7 @@
    541      if (lease->old_hostname)
    542        {	
    543  #ifndef NO_FORK
    544 -	queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
    545 +	queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now, 0);
    546  #endif
    547  	free(lease->old_hostname);
    548  	lease->old_hostname = NULL;
    549 @@ -552,7 +552,7 @@
    550  	(lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
    551        {
    552  #ifndef NO_FORK
    553 -	queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname, now);
    554 +	queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname, now, 0);
    555  #endif
    556  	lease->new = lease->changed = lease->aux_changed = 0;
    557  	
    558 Index: man/dnsmasq.8
    559 ===================================================================
    560 --- man/dnsmasq.8	(revision 696)
    561 +++ man/dnsmasq.8	(revision 821)
    562 @@ -724,12 +724,15 @@
    563  .B \-6 --dhcp-script=<path>
    564  Whenever a new DHCP lease is created, or an old one destroyed, the
    565  binary specified by this option is run. The arguments to the process
    566 -are "add", "old" or "del", the MAC
    567 +are "add", "old", "access" or "del", the MAC
    568  address of the host (or "<null>"), the IP address, and the hostname,
    569  if known. "add" means a lease has been created, "del" means it has
    570  been destroyed, "old" is a notification of an existing lease when
    571  dnsmasq starts or a change to MAC address or hostname of an existing
    572  lease (also, lease length or expiry and client-id, if leasefile-ro is set).
    573 +The "access" keyword means that a request was just received and depending
    574 +on the script exit status request for address will be granted, if exit status
    575 +is zero or not if it is non-zero.
    576  The process is run as root (assuming that dnsmasq was originally run as
    577  root) even if dnsmasq is configured to change UID to an unprivileged user.
    578  The environment is inherited from the invoker of dnsmasq, and if the
    579