]>
git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/helper.c
1 /* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
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.
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.
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/>.
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
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
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
);
43 static unsigned char *grab_extradata_lua(unsigned char *buf
, unsigned char *end
, char *field
);
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
58 unsigned char hwaddr
[DHCP_CHADDR_MAX
];
59 char interface
[IF_NAMESIZE
];
62 static struct script_data
*buf
= NULL
;
63 static size_t bytes_in_buf
= 0, buf_size
= 0;
65 int create_helper(int event_fd
, int err_fd
, uid_t uid
, gid_t gid
, long max_fd
)
69 struct sigaction sigact
;
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)
75 send_event(err_fd
, EVENT_PIPE_ERR
, errno
, NULL
);
81 close(pipefd
[0]); /* close reader side */
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
;
89 sigemptyset(&sigact
.sa_mask
);
90 sigaction(SIGTERM
, &sigact
, NULL
);
91 sigaction(SIGALRM
, &sigact
, NULL
);
93 if (!option_bool(OPT_DEBUG
) && uid
!= 0)
96 if (setgroups(0, &dummy
) == -1 ||
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
);
106 send_event(event_fd
, EVENT_DIE
, 0, NULL
);
108 send_event(err_fd
, EVENT_USER_ERR
, errno
, daemon
->scriptuser
);
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
)
124 #ifdef HAVE_LUASCRIPT
125 if (daemon
->luascript
)
127 const char *lua_err
= NULL
;
131 /* get Lua to load our script file */
132 if (luaL_dofile(lua
, daemon
->luascript
) != 0)
133 lua_err
= lua_tostring(lua
, -1);
136 lua_getglobal(lua
, "lease");
137 if (lua_type(lua
, -1) != LUA_TFUNCTION
)
138 lua_err
= _("lease() function missing in Lua script");
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
);
149 send_event(event_fd
, EVENT_DIE
, 0, NULL
);
151 send_event(err_fd
, EVENT_LUA_ERR
, 0, (char *)lua_err
);
156 lua_pop(lua
, 1); /* remove nil from stack */
157 lua_getglobal(lua
, "init");
158 if (lua_type(lua
, -1) == LUA_TFUNCTION
)
161 lua_pop(lua
, 1); /* remove nil from stack */
165 /* All init done, close our copy of the error pipe, so that main process can return */
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
;
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))
183 #ifdef HAVE_LUASCRIPT
184 if (daemon
->luascript
)
186 lua_getglobal(lua
, "shutdown");
187 if (lua_type(lua
, -1) == LUA_TFUNCTION
)
194 if (data
.action
== ACTION_DEL
)
196 else if (data
.action
== ACTION_ADD
)
198 else if (data
.action
== ACTION_OLD
|| data
.action
== ACTION_OLD_HOSTNAME
)
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
++)
209 p
+= sprintf(p
, "%.2x", data
.hwaddr
[i
]);
210 if (i
!= data
.hwaddr_len
- 1)
211 p
+= sprintf(p
, ":");
214 /* expiry or length into dhcp_buff2 */
215 #ifdef HAVE_BROKEN_RTC
216 sprintf(daemon
->dhcp_buff2
, "%u", data
.length
);
218 sprintf(daemon
->dhcp_buff2
, "%lu", (unsigned long)data
.expires
);
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
)))
226 if (!read_write(pipefd
[0], buf
,
227 data
.hostname_len
+ data
.ed_len
+ data
.clid_len
, 1))
230 /* CLID into packet */
231 for (p
= daemon
->packet
, i
= 0; i
< data
.clid_len
; i
++)
233 p
+= sprintf(p
, "%.2x", buf
[i
]);
234 if (i
!= data
.clid_len
- 1)
235 p
+= sprintf(p
, ":");
238 buf
+= data
.clid_len
;
240 if (data
.hostname_len
!= 0)
243 hostname
= (char *)buf
;
244 hostname
[data
.hostname_len
- 1] = 0;
245 if (!legal_hostname(hostname
))
247 else if ((dot
= strchr(hostname
, '.')))
254 extradata
= buf
+ data
.hostname_len
;
256 #ifdef HAVE_LUASCRIPT
257 if (daemon
->luascript
)
259 lua_getglobal(lua
, "lease"); /* function to call */
260 lua_pushstring(lua
, action_str
); /* arg1 - action */
261 lua_newtable(lua
); /* arg2 - data table */
263 if (data
.clid_len
!= 0)
265 lua_pushstring(lua
, daemon
->packet
);
266 lua_setfield(lua
, -2, "client_id");
269 if (strlen(data
.interface
) != 0)
271 lua_pushstring(lua
, data
.interface
);
272 lua_setfield(lua
, -2, "interface");
275 #ifdef HAVE_BROKEN_RTC
276 lua_pushnumber(lua
, data
.length
);
277 lua_setfield(lua
, -2, "lease_length");
279 lua_pushnumber(lua
, data
.expires
);
280 lua_setfield(lua
, -2, "lease_expires");
285 lua_pushstring(lua
, hostname
);
286 lua_setfield(lua
, -2, "hostname");
291 lua_pushstring(lua
, domain
);
292 lua_setfield(lua
, -2, "domain");
295 end
= extradata
+ data
.ed_len
;
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");
304 for (i
= 0; buf
; i
++)
306 sprintf(daemon
->dhcp_buff2
, "user_class%i", i
);
307 buf
= grab_extradata_lua(buf
, end
, daemon
->dhcp_buff2
);
310 if (data
.giaddr
.s_addr
!= 0)
312 lua_pushstring(lua
, inet_ntoa(data
.giaddr
));
313 lua_setfield(lua
, -2, "relay_address");
316 if (data
.action
!= ACTION_DEL
&& data
.remaining_time
!= 0)
318 lua_pushnumber(lua
, data
.remaining_time
);
319 lua_setfield(lua
, -2, "time_remaining");
322 if (data
.action
== ACTION_OLD_HOSTNAME
&& hostname
)
324 lua_pushstring(lua
, hostname
);
325 lua_setfield(lua
, -2, "old_hostname");
328 lua_pushstring(lua
, daemon
->dhcp_buff
);
329 lua_setfield(lua
, -2, "mac_address");
331 lua_pushstring(lua
, inet_ntoa(data
.addr
));
332 lua_setfield(lua
, -2, "ip_address");
334 lua_call(lua
, 2, 0); /* pass 2 values, expect 0 */
338 /* no script, just lua */
339 if (!daemon
->lease_change_command
)
342 /* possible fork errors are all temporary resource problems */
343 while ((pid
= fork()) == -1 && (errno
== EAGAIN
|| errno
== ENOMEM
))
349 /* wait for child to complete */
352 /* reap our children's children, if necessary */
356 pid_t rc
= wait(&status
);
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
);
368 if (rc
== -1 && errno
!= EINTR
)
375 if (data
.clid_len
!= 0)
376 my_setenv("DNSMASQ_CLIENT_ID", daemon
->packet
, &err
);
378 if (strlen(data
.interface
) != 0)
379 my_setenv("DNSMASQ_INTERFACE", data
.interface
, &err
);
381 #ifdef HAVE_BROKEN_RTC
382 my_setenv("DNSMASQ_LEASE_LENGTH", daemon
->dhcp_buff2
, &err
);
384 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon
->dhcp_buff2
, &err
);
388 my_setenv("DNSMASQ_DOMAIN", domain
, &err
);
390 end
= extradata
+ data
.ed_len
;
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
);
399 for (i
= 0; buf
; i
++)
401 sprintf(daemon
->dhcp_buff2
, "DNSMASQ_USER_CLASS%i", i
);
402 buf
= grab_extradata(buf
, end
, daemon
->dhcp_buff2
, &err
);
405 if (data
.giaddr
.s_addr
!= 0)
406 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data
.giaddr
), &err
);
408 if (data
.action
!= ACTION_DEL
&& data
.remaining_time
!= 0)
410 sprintf(daemon
->dhcp_buff2
, "%u", data
.remaining_time
);
411 my_setenv("DNSMASQ_TIME_REMAINING", daemon
->dhcp_buff2
, &err
);
414 if (data
.action
== ACTION_OLD_HOSTNAME
&& hostname
)
416 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname
, &err
);
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
);
425 p
= strrchr(daemon
->lease_change_command
, '/');
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
);
433 /* failed, send event so the main process logs the problem */
434 send_event(event_fd
, EVENT_EXEC_ERR
, err
, NULL
);
439 static void my_setenv(const char *name
, const char *value
, int *error
)
441 if (*error
== 0 && setenv(name
, value
, 1) != 0)
445 static unsigned char *grab_extradata(unsigned char *buf
, unsigned char *end
, char *env
, int *err
)
449 if (!buf
|| (buf
== end
))
452 for (next
= buf
; *next
!= 0; next
++)
459 /* No "=" in value */
460 if ((p
= strchr((char *)buf
, '=')))
462 my_setenv(env
, (char *)buf
, err
);
468 #ifdef HAVE_LUASCRIPT
469 static unsigned char *grab_extradata_lua(unsigned char *buf
, unsigned char *end
, char *field
)
473 if (!buf
|| (buf
== end
))
476 for (next
= buf
; *next
!= 0; next
++)
482 lua_pushstring(lua
, (char *)buf
);
483 lua_setfield(lua
, -2, field
);
490 /* pack up lease data into a buffer */
491 void queue_script(int action
, struct dhcp_lease
*lease
, char *hostname
, time_t now
)
495 unsigned int hostname_len
= 0, clid_len
= 0, ed_len
= 0;
498 if (daemon
->helperfd
== -1)
501 if (lease
->extradata
)
502 ed_len
= lease
->extradata_len
;
504 clid_len
= lease
->clid_len
;
506 hostname_len
= strlen(hostname
) + 1;
508 size
= sizeof(struct script_data
) + clid_len
+ ed_len
+ hostname_len
;
512 struct script_data
*new;
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;
518 if (!(new = whine_malloc(size
)))
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;
538 #ifdef HAVE_BROKEN_RTC
539 buf
->length
= lease
->length
;
541 buf
->expires
= lease
->expires
;
544 if (lease
->expires
!= 0)
545 buf
->remaining_time
= (unsigned int)difftime(lease
->expires
, now
);
547 buf
->remaining_time
= 0;
549 p
= (unsigned char *)(buf
+1);
552 memcpy(p
, lease
->clid
, clid_len
);
555 if (hostname_len
!= 0)
557 memcpy(p
, hostname
, hostname_len
);
562 memcpy(p
, lease
->extradata
, ed_len
);
565 bytes_in_buf
= p
- (unsigned char *)buf
;
568 int helper_buf_empty(void)
570 return bytes_in_buf
== 0;
573 void helper_write(void)
577 if (bytes_in_buf
== 0)
580 if ((rc
= write(daemon
->helperfd
, buf
, bytes_in_buf
)) != -1)
582 if (bytes_in_buf
!= (size_t)rc
)
583 memmove(buf
, buf
+ rc
, bytes_in_buf
- rc
);
588 if (errno
== EAGAIN
|| errno
== EINTR
)