]> git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/helper.c
Accumulated 2.60 changes going into git
[people/ms/dnsmasq.git] / src / helper.c
1 /* dnsmasq is Copyright (c) 2000-2011 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 #ifdef HAVE_SCRIPT
20
21 /* This file has code to fork a helper process which recieves data via a pipe
22 shared with the main process and which is responsible for calling a script when
23 DHCP leases change.
24
25 The helper process is forked before the main process drops root, so it retains root
26 privs to pass on to the script. For this reason it tries to be paranoid about
27 data received from the main process, in case that has been compromised. We don't
28 want the helper to give an attacker root. In particular, the script to be run is
29 not settable via the pipe, once the fork has taken place it is not alterable by the
30 main process.
31 */
32
33 static void my_setenv(const char *name, const char *value, int *error);
34 static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err);
35
36 #ifdef HAVE_LUASCRIPT
37 #include <lua.h>
38 #include <lualib.h>
39 #include <lauxlib.h>
40
41 lua_State *lua;
42
43 static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
44 #endif
45
46
47 struct script_data
48 {
49 unsigned char action, hwaddr_len, hwaddr_type;
50 unsigned char clid_len, hostname_len, ed_len;
51 struct in_addr addr, giaddr;
52 unsigned int remaining_time;
53 #ifdef HAVE_BROKEN_RTC
54 unsigned int length;
55 #else
56 time_t expires;
57 #endif
58 unsigned char hwaddr[DHCP_CHADDR_MAX];
59 char interface[IF_NAMESIZE];
60 };
61
62 static struct script_data *buf = NULL;
63 static size_t bytes_in_buf = 0, buf_size = 0;
64
65 int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
66 {
67 pid_t pid;
68 int i, pipefd[2];
69 struct sigaction sigact;
70
71 /* create the pipe through which the main program sends us commands,
72 then fork our process. */
73 if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
74 {
75 send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
76 _exit(0);
77 }
78
79 if (pid != 0)
80 {
81 close(pipefd[0]); /* close reader side */
82 return pipefd[1];
83 }
84
85 /* ignore SIGTERM, so that we can clean up when the main process gets hit
86 and SIGALRM so that we can use sleep() */
87 sigact.sa_handler = SIG_IGN;
88 sigact.sa_flags = 0;
89 sigemptyset(&sigact.sa_mask);
90 sigaction(SIGTERM, &sigact, NULL);
91 sigaction(SIGALRM, &sigact, NULL);
92
93 if (!option_bool(OPT_DEBUG) && uid != 0)
94 {
95 gid_t dummy;
96 if (setgroups(0, &dummy) == -1 ||
97 setgid(gid) == -1 ||
98 setuid(uid) == -1)
99 {
100 if (option_bool(OPT_NO_FORK))
101 /* send error to daemon process if no-fork */
102 send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
103 else
104 {
105 /* kill daemon */
106 send_event(event_fd, EVENT_DIE, 0, NULL);
107 /* return error */
108 send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
109 }
110 _exit(0);
111 }
112 }
113
114 /* close all the sockets etc, we don't need them here.
115 Don't close err_fd, in case the lua-init fails.
116 Note that we have to do this before lua init
117 so we don't close any lua fds. */
118 for (max_fd--; max_fd >= 0; max_fd--)
119 if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
120 max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
121 max_fd != event_fd && max_fd != err_fd)
122 close(max_fd);
123
124 #ifdef HAVE_LUASCRIPT
125 if (daemon->luascript)
126 {
127 const char *lua_err = NULL;
128 lua = lua_open();
129 luaL_openlibs(lua);
130
131 /* get Lua to load our script file */
132 if (luaL_dofile(lua, daemon->luascript) != 0)
133 lua_err = lua_tostring(lua, -1);
134 else
135 {
136 lua_getglobal(lua, "lease");
137 if (lua_type(lua, -1) != LUA_TFUNCTION)
138 lua_err = _("lease() function missing in Lua script");
139 }
140
141 if (lua_err)
142 {
143 if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
144 /* send error to daemon process if no-fork */
145 send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
146 else
147 {
148 /* kill daemon */
149 send_event(event_fd, EVENT_DIE, 0, NULL);
150 /* return error */
151 send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
152 }
153 _exit(0);
154 }
155
156 lua_pop(lua, 1); /* remove nil from stack */
157 lua_getglobal(lua, "init");
158 if (lua_type(lua, -1) == LUA_TFUNCTION)
159 lua_call(lua, 0, 0);
160 else
161 lua_pop(lua, 1); /* remove nil from stack */
162 }
163 #endif
164
165 /* All init done, close our copy of the error pipe, so that main process can return */
166 if (err_fd != -1)
167 close(err_fd);
168
169 /* loop here */
170 while(1)
171 {
172 struct script_data data;
173 char *p, *action_str, *hostname = NULL, *domain = NULL;
174 unsigned char *buf = (unsigned char *)daemon->namebuff;
175 unsigned char *end, *extradata, *alloc_buff = NULL;
176 int err = 0;
177
178 free(alloc_buff);
179
180 /* we read zero bytes when pipe closed: this is our signal to exit */
181 if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
182 {
183 #ifdef HAVE_LUASCRIPT
184 if (daemon->luascript)
185 {
186 lua_getglobal(lua, "shutdown");
187 if (lua_type(lua, -1) == LUA_TFUNCTION)
188 lua_call(lua, 0, 0);
189 }
190 #endif
191 _exit(0);
192 }
193
194 if (data.action == ACTION_DEL)
195 action_str = "del";
196 else if (data.action == ACTION_ADD)
197 action_str = "add";
198 else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
199 action_str = "old";
200 else
201 continue;
202
203 /* stringify MAC into dhcp_buff */
204 p = daemon->dhcp_buff;
205 if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
206 p += sprintf(p, "%.2x-", data.hwaddr_type);
207 for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
208 {
209 p += sprintf(p, "%.2x", data.hwaddr[i]);
210 if (i != data.hwaddr_len - 1)
211 p += sprintf(p, ":");
212 }
213
214 /* expiry or length into dhcp_buff2 */
215 #ifdef HAVE_BROKEN_RTC
216 sprintf(daemon->dhcp_buff2, "%u", data.length);
217 #else
218 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
219 #endif
220
221 /* supplied data may just exceed normal buffer (unlikely) */
222 if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
223 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
224 continue;
225
226 if (!read_write(pipefd[0], buf,
227 data.hostname_len + data.ed_len + data.clid_len, 1))
228 continue;
229
230 /* CLID into packet */
231 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
232 {
233 p += sprintf(p, "%.2x", buf[i]);
234 if (i != data.clid_len - 1)
235 p += sprintf(p, ":");
236 }
237
238 buf += data.clid_len;
239
240 if (data.hostname_len != 0)
241 {
242 char *dot;
243 hostname = (char *)buf;
244 hostname[data.hostname_len - 1] = 0;
245 if (!legal_hostname(hostname))
246 hostname = NULL;
247 else if ((dot = strchr(hostname, '.')))
248 {
249 domain = dot+1;
250 *dot = 0;
251 }
252 }
253
254 extradata = buf + data.hostname_len;
255
256 #ifdef HAVE_LUASCRIPT
257 if (daemon->luascript)
258 {
259 lua_getglobal(lua, "lease"); /* function to call */
260 lua_pushstring(lua, action_str); /* arg1 - action */
261 lua_newtable(lua); /* arg2 - data table */
262
263 if (data.clid_len != 0)
264 {
265 lua_pushstring(lua, daemon->packet);
266 lua_setfield(lua, -2, "client_id");
267 }
268
269 if (strlen(data.interface) != 0)
270 {
271 lua_pushstring(lua, data.interface);
272 lua_setfield(lua, -2, "interface");
273 }
274
275 #ifdef HAVE_BROKEN_RTC
276 lua_pushnumber(lua, data.length);
277 lua_setfield(lua, -2, "lease_length");
278 #else
279 lua_pushnumber(lua, data.expires);
280 lua_setfield(lua, -2, "lease_expires");
281 #endif
282
283 if (hostname)
284 {
285 lua_pushstring(lua, hostname);
286 lua_setfield(lua, -2, "hostname");
287 }
288
289 if (domain)
290 {
291 lua_pushstring(lua, domain);
292 lua_setfield(lua, -2, "domain");
293 }
294
295 end = extradata + data.ed_len;
296 buf = extradata;
297 buf = grab_extradata_lua(buf, end, "vendor_class");
298 buf = grab_extradata_lua(buf, end, "supplied_hostname");
299 buf = grab_extradata_lua(buf, end, "cpewan_oui");
300 buf = grab_extradata_lua(buf, end, "cpewan_serial");
301 buf = grab_extradata_lua(buf, end, "cpewan_class");
302 buf = grab_extradata_lua(buf, end, "tags");
303
304 for (i = 0; buf; i++)
305 {
306 sprintf(daemon->dhcp_buff2, "user_class%i", i);
307 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
308 }
309
310 if (data.giaddr.s_addr != 0)
311 {
312 lua_pushstring(lua, inet_ntoa(data.giaddr));
313 lua_setfield(lua, -2, "relay_address");
314 }
315
316 if (data.action != ACTION_DEL && data.remaining_time != 0)
317 {
318 lua_pushnumber(lua, data.remaining_time);
319 lua_setfield(lua, -2, "time_remaining");
320 }
321
322 if (data.action == ACTION_OLD_HOSTNAME && hostname)
323 {
324 lua_pushstring(lua, hostname);
325 lua_setfield(lua, -2, "old_hostname");
326 }
327
328 lua_pushstring(lua, daemon->dhcp_buff);
329 lua_setfield(lua, -2, "mac_address");
330
331 lua_pushstring(lua, inet_ntoa(data.addr));
332 lua_setfield(lua, -2, "ip_address");
333
334 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
335 }
336 #endif
337
338 /* no script, just lua */
339 if (!daemon->lease_change_command)
340 continue;
341
342 /* possible fork errors are all temporary resource problems */
343 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
344 sleep(2);
345
346 if (pid == -1)
347 continue;
348
349 /* wait for child to complete */
350 if (pid != 0)
351 {
352 /* reap our children's children, if necessary */
353 while (1)
354 {
355 int status;
356 pid_t rc = wait(&status);
357
358 if (rc == pid)
359 {
360 /* On error send event back to main process for logging */
361 if (WIFSIGNALED(status))
362 send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
363 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
364 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
365 break;
366 }
367
368 if (rc == -1 && errno != EINTR)
369 break;
370 }
371
372 continue;
373 }
374
375 if (data.clid_len != 0)
376 my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
377
378 if (strlen(data.interface) != 0)
379 my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
380
381 #ifdef HAVE_BROKEN_RTC
382 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
383 #else
384 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
385 #endif
386
387 if (domain)
388 my_setenv("DNSMASQ_DOMAIN", domain, &err);
389
390 end = extradata + data.ed_len;
391 buf = extradata;
392 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
393 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
394 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
395 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
396 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
397 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
398
399 for (i = 0; buf; i++)
400 {
401 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
402 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
403 }
404
405 if (data.giaddr.s_addr != 0)
406 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
407
408 if (data.action != ACTION_DEL && data.remaining_time != 0)
409 {
410 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
411 my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
412 }
413
414 if (data.action == ACTION_OLD_HOSTNAME && hostname)
415 {
416 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
417 hostname = NULL;
418 }
419
420 /* we need to have the event_fd around if exec fails */
421 if ((i = fcntl(event_fd, F_GETFD)) != -1)
422 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
423 close(pipefd[0]);
424
425 p = strrchr(daemon->lease_change_command, '/');
426 if (err == 0)
427 {
428 execl(daemon->lease_change_command,
429 p ? p+1 : daemon->lease_change_command,
430 action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL);
431 err = errno;
432 }
433 /* failed, send event so the main process logs the problem */
434 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
435 _exit(0);
436 }
437 }
438
439 static void my_setenv(const char *name, const char *value, int *error)
440 {
441 if (*error == 0 && setenv(name, value, 1) != 0)
442 *error = errno;
443 }
444
445 static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
446 {
447 unsigned char *next;
448
449 if (!buf || (buf == end))
450 return NULL;
451
452 for (next = buf; *next != 0; next++)
453 if (next == end)
454 return NULL;
455
456 if (next != buf)
457 {
458 char *p;
459 /* No "=" in value */
460 if ((p = strchr((char *)buf, '=')))
461 *p = 0;
462 my_setenv(env, (char *)buf, err);
463 }
464
465 return next + 1;
466 }
467
468 #ifdef HAVE_LUASCRIPT
469 static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
470 {
471 unsigned char *next;
472
473 if (!buf || (buf == end))
474 return NULL;
475
476 for (next = buf; *next != 0; next++)
477 if (next == end)
478 return NULL;
479
480 if (next != buf)
481 {
482 lua_pushstring(lua, (char *)buf);
483 lua_setfield(lua, -2, field);
484 }
485
486 return next + 1;
487 }
488 #endif
489
490 /* pack up lease data into a buffer */
491 void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
492 {
493 unsigned char *p;
494 size_t size;
495 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
496
497 /* no script */
498 if (daemon->helperfd == -1)
499 return;
500
501 if (lease->extradata)
502 ed_len = lease->extradata_len;
503 if (lease->clid)
504 clid_len = lease->clid_len;
505 if (hostname)
506 hostname_len = strlen(hostname) + 1;
507
508 size = sizeof(struct script_data) + clid_len + ed_len + hostname_len;
509
510 if (size > buf_size)
511 {
512 struct script_data *new;
513
514 /* start with reasonable size, will almost never need extending. */
515 if (size < sizeof(struct script_data) + 200)
516 size = sizeof(struct script_data) + 200;
517
518 if (!(new = whine_malloc(size)))
519 return;
520 if (buf)
521 free(buf);
522 buf = new;
523 buf_size = size;
524 }
525
526 buf->action = action;
527 buf->hwaddr_len = lease->hwaddr_len;
528 buf->hwaddr_type = lease->hwaddr_type;
529 buf->clid_len = clid_len;
530 buf->ed_len = ed_len;
531 buf->hostname_len = hostname_len;
532 buf->addr = lease->addr;
533 buf->giaddr = lease->giaddr;
534 memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len);
535 if (!indextoname(daemon->dhcpfd, lease->last_interface, buf->interface))
536 buf->interface[0] = 0;
537
538 #ifdef HAVE_BROKEN_RTC
539 buf->length = lease->length;
540 #else
541 buf->expires = lease->expires;
542 #endif
543
544 if (lease->expires != 0)
545 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
546 else
547 buf->remaining_time = 0;
548
549 p = (unsigned char *)(buf+1);
550 if (clid_len != 0)
551 {
552 memcpy(p, lease->clid, clid_len);
553 p += clid_len;
554 }
555 if (hostname_len != 0)
556 {
557 memcpy(p, hostname, hostname_len);
558 p += hostname_len;
559 }
560 if (ed_len != 0)
561 {
562 memcpy(p, lease->extradata, ed_len);
563 p += ed_len;
564 }
565 bytes_in_buf = p - (unsigned char *)buf;
566 }
567
568 int helper_buf_empty(void)
569 {
570 return bytes_in_buf == 0;
571 }
572
573 void helper_write(void)
574 {
575 ssize_t rc;
576
577 if (bytes_in_buf == 0)
578 return;
579
580 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
581 {
582 if (bytes_in_buf != (size_t)rc)
583 memmove(buf, buf + rc, bytes_in_buf - rc);
584 bytes_in_buf -= rc;
585 }
586 else
587 {
588 if (errno == EAGAIN || errno == EINTR)
589 return;
590 bytes_in_buf = 0;
591 }
592 }
593
594 #endif
595
596
597