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