1 /* dnsmasq is Copyright (c) 2000-2010 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/>.
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
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
31 #if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
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
);
38 unsigned char action
, hwaddr_len
, hwaddr_type
;
39 unsigned char clid_len
, hostname_len
, ed_len
;
40 struct in_addr addr
, giaddr
;
41 unsigned int remaining_time
;
42 #ifdef HAVE_BROKEN_RTC
47 unsigned char hwaddr
[DHCP_CHADDR_MAX
];
48 char interface
[IF_NAMESIZE
];
51 static struct script_data
*buf
= NULL
;
52 static size_t bytes_in_buf
= 0, buf_size
= 0;
54 int create_helper(int event_fd
, int err_fd
, uid_t uid
, gid_t gid
, long max_fd
)
58 struct sigaction sigact
;
60 /* create the pipe through which the main program sends us commands,
61 then fork our process. */
62 if (pipe(pipefd
) == -1 || !fix_fd(pipefd
[1]) || (pid
= fork()) == -1)
64 send_event(err_fd
, EVENT_PIPE_ERR
, errno
);
70 close(pipefd
[0]); /* close reader side */
74 /* ignore SIGTERM, so that we can clean up when the main process gets hit
75 and SIGALRM so that we can use sleep() */
76 sigact
.sa_handler
= SIG_IGN
;
78 sigemptyset(&sigact
.sa_mask
);
79 sigaction(SIGTERM
, &sigact
, NULL
);
80 sigaction(SIGALRM
, &sigact
, NULL
);
82 if (!(daemon
->options
& OPT_DEBUG
) && uid
!= 0)
85 if (setgroups(0, &dummy
) == -1 ||
89 if (daemon
->options
& OPT_NO_FORK
)
90 /* send error to daemon process if no-fork */
91 send_event(event_fd
, EVENT_HUSER_ERR
, errno
);
95 send_event(event_fd
, EVENT_DIE
, 0);
97 send_event(err_fd
, EVENT_HUSER_ERR
, errno
);
103 /* close all the sockets etc, we don't need them here. This closes err_fd, so that
104 main process can return. */
105 for (max_fd
--; max_fd
>= 0; max_fd
--)
106 if (max_fd
!= STDOUT_FILENO
&& max_fd
!= STDERR_FILENO
&&
107 max_fd
!= STDIN_FILENO
&& max_fd
!= pipefd
[0] && max_fd
!= event_fd
)
113 struct script_data data
;
114 char *p
, *action_str
, *hostname
= NULL
;
115 unsigned char *buf
= (unsigned char *)daemon
->namebuff
;
116 unsigned char *end
, *alloc_buff
= NULL
;
119 /* we read zero bytes when pipe closed: this is our signal to exit */
120 if (!read_write(pipefd
[0], (unsigned char *)&data
, sizeof(data
), 1))
123 if (data
.action
== ACTION_DEL
)
125 else if (data
.action
== ACTION_ADD
)
127 else if (data
.action
== ACTION_OLD
|| data
.action
== ACTION_OLD_HOSTNAME
)
132 /* stringify MAC into dhcp_buff */
133 p
= daemon
->dhcp_buff
;
134 if (data
.hwaddr_type
!= ARPHRD_ETHER
|| data
.hwaddr_len
== 0)
135 p
+= sprintf(p
, "%.2x-", data
.hwaddr_type
);
136 for (i
= 0; (i
< data
.hwaddr_len
) && (i
< DHCP_CHADDR_MAX
); i
++)
138 p
+= sprintf(p
, "%.2x", data
.hwaddr
[i
]);
139 if (i
!= data
.hwaddr_len
- 1)
140 p
+= sprintf(p
, ":");
143 /* and CLID into packet, avoid overwrite from bad data */
144 if ((data
.clid_len
> daemon
->packet_buff_sz
) || !read_write(pipefd
[0], buf
, data
.clid_len
, 1))
146 for (p
= daemon
->packet
, i
= 0; i
< data
.clid_len
; i
++)
148 p
+= sprintf(p
, "%.2x", buf
[i
]);
149 if (i
!= data
.clid_len
- 1)
150 p
+= sprintf(p
, ":");
153 /* and expiry or length into dhcp_buff2 */
154 #ifdef HAVE_BROKEN_RTC
155 sprintf(daemon
->dhcp_buff2
, "%u", data
.length
);
157 sprintf(daemon
->dhcp_buff2
, "%lu", (unsigned long)data
.expires
);
160 /* supplied data may just exceed normal buffer (unlikely) */
161 if ((data
.hostname_len
+ data
.ed_len
) > daemon
->packet_buff_sz
&&
162 !(alloc_buff
= buf
= malloc(data
.hostname_len
+ data
.ed_len
)))
165 if (!read_write(pipefd
[0], buf
,
166 data
.hostname_len
+ data
.ed_len
, 1))
169 /* possible fork errors are all temporary resource problems */
170 while ((pid
= fork()) == -1 && (errno
== EAGAIN
|| errno
== ENOMEM
))
178 /* wait for child to complete */
181 /* reap our children's children, if necessary */
185 pid_t rc
= wait(&status
);
189 /* On error send event back to main process for logging */
190 if (WIFSIGNALED(status
))
191 send_event(event_fd
, EVENT_KILLED
, WTERMSIG(status
));
192 else if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0)
193 send_event(event_fd
, EVENT_EXITED
, WEXITSTATUS(status
));
197 if (rc
== -1 && errno
!= EINTR
)
204 if (data
.clid_len
!= 0)
205 my_setenv("DNSMASQ_CLIENT_ID", daemon
->packet
, &err
);
207 if (strlen(data
.interface
) != 0)
208 my_setenv("DNSMASQ_INTERFACE", data
.interface
, &err
);
210 #ifdef HAVE_BROKEN_RTC
211 my_setenv("DNSMASQ_LEASE_LENGTH", daemon
->dhcp_buff2
, &err
);
213 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon
->dhcp_buff2
, &err
);
216 if (data
.hostname_len
!= 0)
219 hostname
= (char *)buf
;
220 hostname
[data
.hostname_len
- 1] = 0;
221 if (!legal_hostname(hostname
))
223 else if ((dot
= strchr(hostname
, '.')))
225 my_setenv("DNSMASQ_DOMAIN", dot
+1, &err
);
228 buf
+= data
.hostname_len
;
231 end
= buf
+ data
.ed_len
;
232 buf
= grab_extradata(buf
, end
, "DNSMASQ_VENDOR_CLASS", &err
);
233 buf
= grab_extradata(buf
, end
, "DNSMASQ_SUPPLIED_HOSTNAME", &err
);
234 buf
= grab_extradata(buf
, end
, "DNSMASQ_CPEWAN_OUI", &err
);
235 buf
= grab_extradata(buf
, end
, "DNSMASQ_CPEWAN_SERIAL", &err
);
236 buf
= grab_extradata(buf
, end
, "DNSMASQ_CPEWAN_CLASS", &err
);
237 buf
= grab_extradata(buf
, end
, "DNSMASQ_TAGS", &err
);
239 for (i
= 0; buf
; i
++)
241 sprintf(daemon
->dhcp_buff2
, "DNSMASQ_USER_CLASS%i", i
);
242 buf
= grab_extradata(buf
, end
, daemon
->dhcp_buff2
, &err
);
245 if (data
.giaddr
.s_addr
!= 0)
246 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data
.giaddr
), &err
);
248 if (data
.action
!= ACTION_DEL
)
250 sprintf(daemon
->dhcp_buff2
, "%u", data
.remaining_time
);
251 my_setenv("DNSMASQ_TIME_REMAINING", daemon
->dhcp_buff2
, &err
);
254 if (data
.action
== ACTION_OLD_HOSTNAME
&& hostname
)
256 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname
, &err
);
260 /* we need to have the event_fd around if exec fails */
261 if ((i
= fcntl(event_fd
, F_GETFD
)) != -1)
262 fcntl(event_fd
, F_SETFD
, i
| FD_CLOEXEC
);
265 p
= strrchr(daemon
->lease_change_command
, '/');
268 execl(daemon
->lease_change_command
,
269 p
? p
+1 : daemon
->lease_change_command
,
270 action_str
, daemon
->dhcp_buff
, inet_ntoa(data
.addr
), hostname
, (char*)NULL
);
273 /* failed, send event so the main process logs the problem */
274 send_event(event_fd
, EVENT_EXEC_ERR
, err
);
279 static void my_setenv(const char *name
, const char *value
, int *error
)
281 if (*error
== 0 && setenv(name
, value
, 1) != 0)
285 static unsigned char *grab_extradata(unsigned char *buf
, unsigned char *end
, char *env
, int *err
)
289 if (!buf
|| (buf
== end
))
292 for (next
= buf
; *next
!= 0; next
++)
299 /* No "=" in value */
300 if ((p
= strchr((char *)buf
, '=')))
302 my_setenv(env
, (char *)buf
, err
);
308 /* pack up lease data into a buffer */
309 void queue_script(int action
, struct dhcp_lease
*lease
, char *hostname
, time_t now
)
313 unsigned int hostname_len
= 0, clid_len
= 0, ed_len
= 0;
316 if (daemon
->helperfd
== -1)
319 if (lease
->extradata
)
320 ed_len
= lease
->extradata_len
;
322 clid_len
= lease
->clid_len
;
324 hostname_len
= strlen(hostname
) + 1;
326 size
= sizeof(struct script_data
) + clid_len
+ ed_len
+ hostname_len
;
330 struct script_data
*new;
332 /* start with reasonable size, will almost never need extending. */
333 if (size
< sizeof(struct script_data
) + 200)
334 size
= sizeof(struct script_data
) + 200;
336 if (!(new = whine_malloc(size
)))
344 buf
->action
= action
;
345 buf
->hwaddr_len
= lease
->hwaddr_len
;
346 buf
->hwaddr_type
= lease
->hwaddr_type
;
347 buf
->clid_len
= clid_len
;
348 buf
->ed_len
= ed_len
;
349 buf
->hostname_len
= hostname_len
;
350 buf
->addr
= lease
->addr
;
351 buf
->giaddr
= lease
->giaddr
;
352 memcpy(buf
->hwaddr
, lease
->hwaddr
, lease
->hwaddr_len
);
353 if (!indextoname(daemon
->dhcpfd
, lease
->last_interface
, buf
->interface
))
354 buf
->interface
[0] = 0;
356 #ifdef HAVE_BROKEN_RTC
357 buf
->length
= lease
->length
;
359 buf
->expires
= lease
->expires
;
361 buf
->remaining_time
= (unsigned int)difftime(lease
->expires
, now
);
363 p
= (unsigned char *)(buf
+1);
366 memcpy(p
, lease
->clid
, clid_len
);
369 if (hostname_len
!= 0)
371 memcpy(p
, hostname
, hostname_len
);
376 memcpy(p
, lease
->extradata
, ed_len
);
379 bytes_in_buf
= p
- (unsigned char *)buf
;
382 int helper_buf_empty(void)
384 return bytes_in_buf
== 0;
387 void helper_write(void)
391 if (bytes_in_buf
== 0)
394 if ((rc
= write(daemon
->helperfd
, buf
, bytes_in_buf
)) != -1)
396 if (bytes_in_buf
!= (size_t)rc
)
397 memmove(buf
, buf
+ rc
, bytes_in_buf
- rc
);
402 if (errno
== EAGAIN
|| errno
== EINTR
)