Skip to content

Commit d0282e1

Browse files
committed
dhcpsd: don't have a controlling TTY on fork
1 parent 55038a4 commit d0282e1

File tree

3 files changed

+167
-25
lines changed

3 files changed

+167
-25
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ end
285285
function commit_lease(hostname, htype, chaddr, clientid, ip, flags, leased, expires)
286286
-- NOTE: dhcpsd MUST be supplied the debug flag to keep stdout open
287287
local rflags = ""
288-
local type = "UNKNOWN"
288+
local type = "STORED"
289289
if string.find(flags, "D") ~= nil or string.find(flags, "d") ~= nil then
290290
type = "DECLINED"
291291
elseif string.find(flags, "O") ~= nil then

src/dhcpsd.c

Lines changed: 161 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,149 @@ dhcpsd_mkdbdir(void)
158158
return 0;
159159
}
160160

161+
static void
162+
dhcpsd_fork_cb(void *arg, unsigned short e)
163+
{
164+
struct ctx *ctx = arg;
165+
int exit_code;
166+
ssize_t nread;
167+
168+
if (e & ELE_HANGUP) {
169+
hangup:
170+
close(ctx->ctx_fork_fd);
171+
ctx->ctx_fork_fd = -1;
172+
eloop_exit(ctx->ctx_eloop, EXIT_FAILURE);
173+
return;
174+
}
175+
nread = recv(ctx->ctx_fork_fd, &exit_code, sizeof(exit_code), 0);
176+
if (nread == 0)
177+
goto hangup;
178+
if (nread == -1) {
179+
logerr("%s: recv %d", __func__, ctx->ctx_fork_fd);
180+
goto hangup;
181+
}
182+
if ((size_t)nread != sizeof(exit_code)) {
183+
logerrx("%s: truncated %zd", __func__, nread);
184+
goto hangup;
185+
}
186+
eloop_exit(ctx->ctx_eloop, exit_code);
187+
}
188+
189+
/* Complicated, but it ensures we don't get a controlling terminal
190+
* and the ppid of any child processed match the main process. */
191+
static int
192+
dhcpsd_fork(struct ctx *ctx)
193+
{
194+
int fork_fd[2];
195+
pid_t pid;
196+
#ifdef HAVE_CASPER
197+
cap_rights_t rights;
198+
#endif
199+
200+
if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK,
201+
0, fork_fd) == -1) {
202+
logerr("socketpair");
203+
return -1;
204+
}
205+
206+
switch (pid = fork()) {
207+
case -1:
208+
logerr("%s: fork", __func__);
209+
return -1;
210+
;
211+
case 0:
212+
ctx->ctx_fork_fd = fork_fd[1];
213+
close(fork_fd[0]);
214+
#ifdef HAVE_CASPER
215+
cap_rights_init(&rights, CAP_WRITE);
216+
if (caph_rights_limit(ctx->ctx_fork_fd, &rights) == -1) {
217+
logerr("%s: caph_rights_limit", __func__);
218+
return -1;
219+
}
220+
#endif
221+
if (setsid() == -1) {
222+
logerr("%s: setsid", __func__);
223+
return -1;
224+
}
225+
/* Ensure we can never get a controlling terminal */
226+
switch (pid = fork()) {
227+
case -1:
228+
logerr("%s: fork", __func__);
229+
return -1;
230+
case 0:
231+
/* setsid again to ensure our child processes have the
232+
* correct ppid */
233+
if (setsid() == -1) {
234+
logerr("%s: setsid", __func__);
235+
return -1;
236+
}
237+
if (eloop_forked(ctx->ctx_eloop, ELF_KEEP_ALL) == -1) {
238+
logerr("%s: eloop_forked", __func__);
239+
return -1;
240+
}
241+
if (eloop_event_add(ctx->ctx_eloop, ctx->ctx_fork_fd,
242+
ELE_READ, dhcpsd_fork_cb, ctx) == -1) {
243+
logerr("%s: eloop_event_add", __func__);
244+
return -1;
245+
}
246+
break;
247+
default:
248+
ctx->ctx_options &= ~DHCPSD_MAIN;
249+
break;
250+
}
251+
break;
252+
default:
253+
#ifdef BSD
254+
setproctitle("[launcher]");
255+
#endif
256+
ctx->ctx_options &= ~DHCPSD_MAIN;
257+
ctx->ctx_options |= DHCPSD_LAUNCHER;
258+
ctx->ctx_fork_fd = fork_fd[0];
259+
close(fork_fd[1]);
260+
261+
#ifdef HAVE_CASPER
262+
cap_rights_init(&rights, CAP_READ);
263+
if (caph_rights_limit(ctx->ctx_fork_fd, &rights) == -1) {
264+
logerr("%s: caph_rights_limit", __func__);
265+
return -1;
266+
}
267+
#endif
268+
if (eloop_event_add(ctx->ctx_eloop, ctx->ctx_fork_fd, ELE_READ,
269+
dhcpsd_fork_cb, ctx) == -1) {
270+
logerr("%s: eloop_event_add", __func__);
271+
return -1;
272+
}
273+
break;
274+
}
275+
return 0;
276+
}
277+
278+
static void
279+
dhcpsd_send_launcher(struct ctx *ctx, int exit_code)
280+
{
281+
if (ctx->ctx_fork_fd == -1)
282+
return;
283+
if (send(ctx->ctx_fork_fd, &exit_code, sizeof(exit_code), MSG_EOR) ==
284+
-1)
285+
logerr("%s: sendmsg", __func__);
286+
close(ctx->ctx_fork_fd);
287+
ctx->ctx_fork_fd = -1;
288+
}
289+
161290
int
162291
main(int argc, char **argv)
163292
{
164293
struct if_head ifaces;
165294
struct ctx ctx = {
166295
.ctx_options = DHCPSD_MAIN,
167296
.ctx_ifaces = &ifaces,
297+
.ctx_fork_fd = -1,
168298
.ctx_pf_inet_fd = -1,
169299
#ifdef IFLR_ACTIVE
170300
.ctx_pf_link_fd = -1,
171301
#endif
172302
};
173-
int ch, exit_code = EXIT_FAILURE;
303+
int ch, exit_code = EXIT_FAILURE, f_flag = 0;
174304
size_t npools;
175305
struct interface *ifp;
176306
unsigned int logopts = LOGERR_LOG;
@@ -184,13 +314,15 @@ main(int argc, char **argv)
184314
TAILQ_INIT(ctx.ctx_ifaces);
185315
closefrom(STDERR_FILENO + 1);
186316

317+
logopts |= LOGERR_ERR; // log to stderr
187318
#define OPTS "dfp:"
188319
while ((ch = getopt(argc, argv, OPTS)) != -1) {
189320
switch (ch) {
190321
case 'd':
191322
logopts |= LOGERR_DEBUG;
192323
break;
193324
case 'f':
325+
f_flag = 1;
194326
logopts |= LOGERR_ERR; // log to stderr
195327
logopts &= ~LOGERR_LOG; // don't syslog
196328
break;
@@ -200,17 +332,28 @@ main(int argc, char **argv)
200332
logsetopts(logopts);
201333
logopen(NULL);
202334

203-
loginfox(PACKAGE "-" VERSION " starting");
204-
if (dhcpsd_mkdbdir() == -1)
205-
goto exit;
206-
207335
ctx.ctx_eloop = eloop_new();
208336
if (ctx.ctx_eloop == NULL) {
209337
logerr("%s: eloop_new", __func__);
210338
goto exit;
211339
}
212-
eloop_signal_set_cb(ctx.ctx_eloop, signals, signals_len,
213-
dhcpsd_signal_cb, &ctx);
340+
if (eloop_signal_set_cb(ctx.ctx_eloop, signals, signals_len,
341+
dhcpsd_signal_cb, &ctx) == -1) {
342+
logerr("%s: eloop_signal_set_cb", __func__);
343+
goto exit;
344+
}
345+
346+
if (!(f_flag) && dhcpsd_fork(&ctx) == -1)
347+
goto exit;
348+
if (ctx.ctx_options & DHCPSD_LAUNCHER)
349+
goto run;
350+
if (!(ctx.ctx_options & DHCPSD_MAIN))
351+
goto exit;
352+
353+
loginfox(PACKAGE "-" VERSION " starting");
354+
if (dhcpsd_mkdbdir() == -1)
355+
goto exit;
356+
214357
if (eloop_signal_mask(ctx.ctx_eloop) == -1) {
215358
logerr("%s: eloop_signal_mask", __func__);
216359
goto exit;
@@ -346,17 +489,6 @@ main(int argc, char **argv)
346489
if (dhcpsd_dropperms(1) == -1)
347490
goto exit;
348491

349-
/* If we separate -f from no syslog we need a new variable */
350-
if (logopts & LOGERR_LOG) {
351-
if (daemon(0, 0) == -1) {
352-
logerr("%s: daemon", __func__);
353-
goto exit;
354-
}
355-
if (eloop_forked(ctx.ctx_eloop, ELF_KEEP_ALL) == -1) {
356-
logerr("%s: eloop_forked", __func__);
357-
goto exit;
358-
}
359-
}
360492
#ifdef BSD
361493
setproctitle("DHCP Server Daemon");
362494
#endif
@@ -446,15 +578,19 @@ main(int argc, char **argv)
446578
#endif
447579

448580
loginfox(PACKAGE " is now running");
581+
if (ctx.ctx_fork_fd != -1) {
582+
dhcpsd_send_launcher(&ctx, EXIT_SUCCESS);
583+
logopts &= ~LOGERR_ERR; // stop logging to stderr
584+
logsetopts(logopts);
585+
}
449586
dhcp_expire_leases(ctx.ctx_dhcp);
450587

451588
run:
452589
exit_code = eloop_start(ctx.ctx_eloop);
453590
if (exit_code < 0) {
454591
logerr("%s: eloop_start", __func__);
455592
exit_code = EXIT_FAILURE;
456-
} else
457-
exit_code = EXIT_SUCCESS;
593+
}
458594

459595
if (dhcpsd_store_leases(&ctx) == -1)
460596
exit_code = EXIT_FAILURE;
@@ -469,12 +605,16 @@ main(int argc, char **argv)
469605
}
470606
dhcp_free(ctx.ctx_dhcp);
471607
eloop_free(ctx.ctx_eloop);
608+
ctx.ctx_eloop = NULL;
472609
svc_free(ctx.ctx_unpriv);
473610
#ifdef HAVE_CASPER
474611
if (ctx.ctx_capnet)
475612
cap_close(ctx.ctx_capnet);
476613
#endif
477-
if (ctx.ctx_options & DHCPSD_MAIN)
614+
if (ctx.ctx_options & DHCPSD_MAIN) {
478615
logdebugx("dhcpsd exited");
616+
dhcpsd_send_launcher(&ctx, exit_code);
617+
}
618+
479619
return exit_code;
480620
}

src/dhcpsd.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ struct ctx {
5252
size_t ctx_nplugins;
5353

5454
unsigned int ctx_options;
55-
#define DHCPSD_RUN (1U << 0) /* Set by forked stuff */
56-
#define DHCPSD_MAIN (1U << 1) /* Main process */
57-
#define DHCPSD_UNPRIV (1U << 2) /* Unprivileged helper */
55+
#define DHCPSD_RUN (1U << 0) /* Set by forked stuff */
56+
#define DHCPSD_MAIN (1U << 1) /* Main process */
57+
#define DHCPSD_UNPRIV (1U << 2) /* Unprivileged helper */
58+
#define DHCPSD_LAUNCHER (1U << 3) /* Launcher process */
5859

60+
int ctx_fork_fd;
5961
int ctx_pf_inet_fd;
6062
#ifdef IFLR_ACTIVE
6163
int ctx_pf_link_fd;

0 commit comments

Comments
 (0)