1 /* dnsmasq is Copyright (c) 2000-2009 Simon Kelley 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License as published by 5 the Free Software Foundation; version 2 dated June, 1991, or 6 (at your option) version 3 dated 29 June, 2007. 7 8 This program is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License 14 along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include "dnsmasq.h" 18 19 /* This file has code to fork a helper process which recieves data via a pipe 20 shared with the main process and which is responsible for calling a script when 21 DHCP leases change. 22 23 The helper process is forked before the main process drops root, so it retains root 24 privs to pass on to the script. For this reason it tries to be paranoid about 25 data received from the main process, in case that has been compromised. We don't 26 want the helper to give an attacker root. In particular, the script to be run is 27 not settable via the pipe, once the fork has taken place it is not alterable by the 28 main process. 29 */ 30 31 #if defined(HAVE_DHCP) && defined(HAVE_SCRIPT) 32 33 static void my_setenv(const char *name, const char *value, int *error); 34 35 struct script_data 36 { 37 unsigned char action, hwaddr_len, hwaddr_type; 38 unsigned char clid_len, hostname_len, uclass_len, vclass_len, shost_len; 39 struct in_addr addr, giaddr; 40 unsigned int remaining_time; 41 #ifdef HAVE_BROKEN_RTC 42 unsigned int length; 43 #else 44 time_t expires; 45 #endif 46 unsigned char hwaddr[DHCP_CHADDR_MAX]; 47 char interface[IF_NAMESIZE]; 48 }; 49 50 static struct script_data *buf = NULL; 51 static size_t bytes_in_buf = 0, buf_size = 0; 52 53 int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) 54 { 55 pid_t pid; 56 int i, pipefd[2]; 57 struct sigaction sigact; 58 59 /* create the pipe through which the main program sends us commands, 60 then fork our process. */ 61 if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1) 62 { 63 send_event(err_fd, EVENT_PIPE_ERR, errno); 64 _exit(0); 65 } 66 67 if (pid != 0) 68 { 69 close(pipefd[0]); /* close reader side */ 70 return pipefd[1]; 71 } 72 73 /* ignore SIGTERM, so that we can clean up when the main process gets hit 74 and SIGALRM so that we can use sleep() */ 75 sigact.sa_handler = SIG_IGN; 76 sigact.sa_flags = 0; 77 sigemptyset(&sigact.sa_mask); 78 sigaction(SIGTERM, &sigact, NULL); 79 sigaction(SIGALRM, &sigact, NULL); 80 81 if (!(daemon->options & OPT_DEBUG) && uid != 0) 82 { 83 gid_t dummy; 84 if (setgroups(0, &dummy) == -1 || 85 setgid(gid) == -1 || 86 setuid(uid) == -1) 87 { 88 if (daemon->options & OPT_NO_FORK) 89 /* send error to daemon process if no-fork */ 90 send_event(event_fd, EVENT_HUSER_ERR, errno); 91 else 92 { 93 /* kill daemon */ 94 send_event(event_fd, EVENT_DIE, 0); 95 /* return error */ 96 send_event(err_fd, EVENT_HUSER_ERR, errno); 97 } 98 _exit(0); 99 } 100 } 101 102 /* close all the sockets etc, we don't need them here. This closes err_fd, so that 103 main process can return. */ 104 for (max_fd--; max_fd >= 0; max_fd--) 105 if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && 106 max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd) 107 close(max_fd); 108 109 /* loop here */ 110 while(1) 111 { 112 struct script_data data; 113 char *p, *action_str, *hostname = NULL; 114 unsigned char *buf = (unsigned char *)daemon->namebuff; 115 int err = 0; 116 117 /* we read zero bytes when pipe closed: this is our signal to exit */ 118 if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1)) 119 _exit(0); 120 121 if (data.action == ACTION_DEL) 122 action_str = "del"; 123 else if (data.action == ACTION_ADD) 124 action_str = "add"; 125 else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME) 126 action_str = "old"; 127 else 128 continue; 129 130 /* stringify MAC into dhcp_buff */ 131 p = daemon->dhcp_buff; 132 if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0) 133 p += sprintf(p, "%.2x-", data.hwaddr_type); 134 for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++) 135 { 136 p += sprintf(p, "%.2x", data.hwaddr[i]); 137 if (i != data.hwaddr_len - 1) 138 p += sprintf(p, ":"); 139 } 140 141 /* and CLID into packet */ 142 if (!read_write(pipefd[0], buf, data.clid_len, 1)) 143 continue; 144 for (p = daemon->packet, i = 0; i < data.clid_len; i++) 145 { 146 p += sprintf(p, "%.2x", buf[i]); 147 if (i != data.clid_len - 1) 148 p += sprintf(p, ":"); 149 } 150 151 /* and expiry or length into dhcp_buff2 */ 152 #ifdef HAVE_BROKEN_RTC 153 sprintf(daemon->dhcp_buff2, "%u ", data.length); 154 #else 155 sprintf(daemon->dhcp_buff2, "%lu ", (unsigned long)data.expires); 156 #endif 157 158 if (!read_write(pipefd[0], buf, 159 data.hostname_len + data.uclass_len + data.vclass_len + data.shost_len, 1)) 160 continue; 161 162 /* possible fork errors are all temporary resource problems */ 163 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM)) 164 sleep(2); 165 166 if (pid == -1) 167 continue; 168 169 /* wait for child to complete */ 170 if (pid != 0) 171 { 172 /* reap our children's children, if necessary */ 173 while (1) 174 { 175 int status; 176 pid_t rc = wait(&status); 177 178 if (rc == pid) 179 { 180 /* On error send event back to main process for logging */ 181 if (WIFSIGNALED(status)) 182 send_event(event_fd, EVENT_KILLED, WTERMSIG(status)); 183 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) 184 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status)); 185 break; 186 } 187 188 if (rc == -1 && errno != EINTR) 189 break; 190 } 191 192 continue; 193 } 194 195 if (data.clid_len != 0) 196 my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err); 197 198 if (strlen(data.interface) != 0) 199 my_setenv("DNSMASQ_INTERFACE", data.interface, &err); 200 201 #ifdef HAVE_BROKEN_RTC 202 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err); 203 #else 204 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err); 205 #endif 206 207 if (data.vclass_len != 0) 208 { 209 buf[data.vclass_len - 1] = 0; /* don't trust zero-term */ 210 /* cannot have = chars in env - truncate if found . */ 211 if ((p = strchr((char *)buf, '='))) 212 *p = 0; 213 my_setenv("DNSMASQ_VENDOR_CLASS", (char *)buf, &err); 214 buf += data.vclass_len; 215 } 216 217 if (data.uclass_len != 0) 218 { 219 unsigned char *end = buf + data.uclass_len; 220 buf[data.uclass_len - 1] = 0; /* don't trust zero-term */ 221 222 for (i = 0; buf < end;) 223 { 224 size_t len = strlen((char *)buf) + 1; 225 if ((p = strchr((char *)buf, '='))) 226 *p = 0; 227 if (strlen((char *)buf) != 0) 228 { 229 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i++); 230 my_setenv(daemon->dhcp_buff2, (char *)buf, &err); 231 } 232 buf += len; 233 } 234 } 235 236 if (data.shost_len != 0) 237 { 238 buf[data.shost_len - 1] = 0; /* don't trust zero-term */ 239 /* cannot have = chars in env - truncate if found . */ 240 if ((p = strchr((char *)buf, '='))) 241 *p = 0; 242 my_setenv("DNSMASQ_SUPPLIED_HOSTNAME", (char *)buf, &err); 243 buf += data.shost_len; 244 } 245 246 if (data.giaddr.s_addr != 0) 247 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err); 248 249 sprintf(daemon->dhcp_buff2, "%u ", data.remaining_time); 250 my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err); 251 252 if (data.hostname_len != 0) 253 { 254 char *dot; 255 hostname = (char *)buf; 256 hostname[data.hostname_len - 1] = 0; 257 if (!legal_hostname(hostname)) 258 hostname = NULL; 259 else if ((dot = strchr(hostname, '.'))) 260 { 261 my_setenv("DNSMASQ_DOMAIN", dot+1, &err); 262 *dot = 0; 263 } 264 } 265 266 if (data.action == ACTION_OLD_HOSTNAME && hostname) 267 { 268 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err); 269 hostname = NULL; 270 } 271 272 /* we need to have the event_fd around if exec fails */ 273 if ((i = fcntl(event_fd, F_GETFD)) != -1) 274 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC); 275 close(pipefd[0]); 276 277 p = strrchr(daemon->lease_change_command, '/'); 278 if (err == 0) 279 { 280 execl(daemon->lease_change_command, 281 p ? p+1 : daemon->lease_change_command, 282 action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL); 283 err = errno; 284 } 285 /* failed, send event so the main process logs the problem */ 286 send_event(event_fd, EVENT_EXEC_ERR, err); 287 _exit(0); 288 } 289 } 290 291 static void my_setenv(const char *name, const char *value, int *error) 292 { 293 if (*error == 0 && setenv(name, value, 1) != 0) 294 *error = errno; 295 } 296 297 /* pack up lease data into a buffer */ 298 void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now) 299 { 300 unsigned char *p; 301 size_t size; 302 unsigned int hostname_len = 0, clid_len = 0, vclass_len = 0; 303 unsigned int uclass_len = 0, shost_len = 0; 304 305 /* no script */ 306 if (daemon->helperfd == -1) 307 return; 308 309 if (lease->vendorclass) 310 vclass_len = lease->vendorclass_len; 311 if (lease->userclass) 312 uclass_len = lease->userclass_len; 313 if (lease->supplied_hostname) 314 shost_len = lease->supplied_hostname_len; 315 if (lease->clid) 316 clid_len = lease->clid_len; 317 if (hostname) 318 hostname_len = strlen(hostname) + 1; 319 320 size = sizeof(struct script_data) + clid_len + vclass_len + uclass_len + shost_len + hostname_len; 321 322 if (size > buf_size) 323 { 324 struct script_data *new; 325 326 /* start with reasonable size, will almost never need extending. */ 327 if (size < sizeof(struct script_data) + 200) 328 size = sizeof(struct script_data) + 200; 329 330 if (!(new = whine_malloc(size))) 331 return; 332 if (buf) 333 free(buf); 334 buf = new; 335 buf_size = size; 336 } 337 338 buf->action = action; 339 buf->hwaddr_len = lease->hwaddr_len; 340 buf->hwaddr_type = lease->hwaddr_type; 341 buf->clid_len = clid_len; 342 buf->vclass_len = vclass_len; 343 buf->uclass_len = uclass_len; 344 buf->shost_len = shost_len; 345 buf->hostname_len = hostname_len; 346 buf->addr = lease->addr; 347 buf->giaddr = lease->giaddr; 348 memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len); 349 buf->interface[0] = 0; 350 #ifdef HAVE_LINUX_NETWORK 351 if (lease->last_interface != 0) 352 { 353 struct ifreq ifr; 354 ifr.ifr_ifindex = lease->last_interface; 355 if (ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) != -1) 356 strncpy(buf->interface, ifr.ifr_name, IF_NAMESIZE); 357 } 358 #else 359 if (lease->last_interface != 0) 360 if_indextoname(lease->last_interface, buf->interface); 361 #endif 362 363 #ifdef HAVE_BROKEN_RTC 364 buf->length = lease->length; 365 #else 366 buf->expires = lease->expires; 367 #endif 368 buf->remaining_time = (unsigned int)difftime(lease->expires, now); 369 370 p = (unsigned char *)(buf+1); 371 if (clid_len != 0) 372 { 373 memcpy(p, lease->clid, clid_len); 374 p += clid_len; 375 } 376 if (vclass_len != 0) 377 { 378 memcpy(p, lease->vendorclass, vclass_len); 379 p += vclass_len; 380 } 381 if (uclass_len != 0) 382 { 383 memcpy(p, lease->userclass, uclass_len); 384 p += uclass_len; 385 } 386 if (shost_len != 0) 387 { 388 memcpy(p, lease->supplied_hostname, shost_len); 389 p += shost_len; 390 } 391 if (hostname_len != 0) 392 { 393 memcpy(p, hostname, hostname_len); 394 p += hostname_len; 395 } 396 397 bytes_in_buf = p - (unsigned char *)buf; 398 } 399 400 int helper_buf_empty(void) 401 { 402 return bytes_in_buf == 0; 403 } 404 405 void helper_write(void) 406 { 407 ssize_t rc; 408 409 if (bytes_in_buf == 0) 410 return; 411 412 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1) 413 { 414 if (bytes_in_buf != (size_t)rc) 415 memmove(buf, buf + rc, bytes_in_buf - rc); 416 bytes_in_buf -= rc; 417 } 418 else 419 { 420 if (errno == EAGAIN || errno == EINTR) 421 return; 422 bytes_in_buf = 0; 423 } 424 } 425 426 #endif 427 428 429