# HG changeset patch # User Roy Marples # Date 1599938087 -3600 # Node ID 68ef863871d110ef356ce227b19bad61ce2e9dac # Parent fef58462dea6d4552b332c4f356ce7d48df79a43 dhcpcd: Only manipulate stdin, stdout and stderr when valid UNIX application expect these to exist even if pointed at /dev/null. We cannot change which fd they use, it's always 0, 1 and 2. But if these fd's are not open when dhcpcd is called, they could be assigned to dhcpcd internals. In this instance we should not use the streams in anyway or form. diff -r fef58462dea6 -r 68ef863871d1 src/dhcpcd.c --- a/src/dhcpcd.c Sat Sep 12 15:58:03 2020 +0100 +++ b/src/dhcpcd.c Sat Sep 12 20:14:47 2020 +0100 @@ -360,7 +360,7 @@ return; /* Don't use loginfo because this makes no sense in a log. */ - if (!(logopts & LOGERR_QUIET)) + if (!(logopts & LOGERR_QUIET) && ctx->stderr_valid) (void)fprintf(stderr, "forked to background, child pid %d\n", getpid()); i = EXIT_SUCCESS; @@ -1893,9 +1893,16 @@ ctx.ps_inet_fd = ctx.ps_control_fd = -1; TAILQ_INIT(&ctx.ps_processes); #endif - rt_init(&ctx); - logopts = LOGERR_ERR|LOGERR_LOG|LOGERR_LOG_DATE|LOGERR_LOG_PID; + /* Check our streams for validity */ + ctx.stdin_valid = fcntl(STDIN_FILENO, F_GETFD) != -1; + ctx.stdout_valid = fcntl(STDOUT_FILENO, F_GETFD) != -1; + ctx.stderr_valid = fcntl(STDERR_FILENO, F_GETFD) != -1; + + logopts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID; + if (ctx.stderr_valid) + logopts |= LOGERR_ERR; + i = 0; while ((opt = getopt_long(argc, argv, ctx.options & DHCPCD_PRINT_PIDFILE ? NOERR_IF_OPTS : IF_OPTS, @@ -1979,6 +1986,8 @@ ctx.ifc = argc - optind; ctx.ifv = argv + optind; + rt_init(&ctx); + ifo = read_config(&ctx, NULL, NULL, NULL); if (ifo == NULL) { if (ctx.options & DHCPCD_PRINT_PIDFILE) @@ -2256,12 +2265,13 @@ } loginfox(PACKAGE "-" VERSION " starting"); - if (freopen(_PATH_DEVNULL, "r", stdin) == NULL) - logerr("%s: freopen stdin", __func__); + if (ctx.stdin_valid && freopen(_PATH_DEVNULL, "w", stdin) == NULL) + logwarn("freopen stdin"); #if defined(USE_SIGNALS) && !defined(THERE_IS_NO_FORK) if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fork_fd) == -1 || - xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, stderr_fd) == -1) + (ctx.stderr_valid && + xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, stderr_fd) == -1)) { logerr("socketpair"); goto exit_failure; @@ -2284,15 +2294,18 @@ * Redirect stdout as well. * dhcpcd doesn't output via stdout, but something in * a called script might. - * - * Do NOT rights limit this fd as it will affect scripts. - * For example, cmp reports insufficient caps on FreeBSD. */ - if (dup2(stderr_fd[1], STDERR_FILENO) == -1 || - dup2(stderr_fd[1], STDOUT_FILENO) == -1) - logerr("dup2"); - close(stderr_fd[0]); - close(stderr_fd[1]); + if (ctx.stderr_valid) { + if (dup2(stderr_fd[1], STDERR_FILENO) == -1 || + (ctx.stdout_valid && + dup2(stderr_fd[1], STDOUT_FILENO) == -1)) + logerr("dup2"); + close(stderr_fd[0]); + close(stderr_fd[1]); + } else if (ctx.stdout_valid) { + if (freopen(_PATH_DEVNULL, "w", stdout) == NULL) + logerr("freopen stdout"); + } if (setsid() == -1) { logerr("%s: setsid", __func__); goto exit_failure; @@ -2312,10 +2325,9 @@ break; default: ctx.options |= DHCPCD_FORKED; /* A lie */ + setproctitle("[launcher]"); ctx.fork_fd = fork_fd[0]; close(fork_fd[1]); - ctx.stderr_fd = stderr_fd[0]; - close(stderr_fd[1]); #ifdef PRIVSEP_RIGHTS if (ps_rights_limit_fd(fork_fd[0]) == -1 || ps_rights_limit_fd(stderr_fd[0]) == 1) @@ -2324,10 +2336,21 @@ goto exit_failure; } #endif - setproctitle("[launcher]"); eloop_event_add(ctx.eloop, ctx.fork_fd, dhcpcd_fork_cb, &ctx); - eloop_event_add(ctx.eloop, ctx.stderr_fd, dhcpcd_stderr_cb, - &ctx); + + if (ctx.stderr_valid) { + ctx.stderr_fd = stderr_fd[0]; + close(stderr_fd[1]); +#ifdef PRIVSEP_RIGHTS + if (ps_rights_limit_fd(stderr_fd[0]) == 1) { + logerr("ps_rights_limit_fdpair"); + goto exit_failure; + } +#endif + if (ctx.stderr_valid) + eloop_event_add(ctx.eloop, ctx.stderr_fd, + dhcpcd_stderr_cb, &ctx); + } goto run_loop; } diff -r fef58462dea6 -r 68ef863871d1 src/dhcpcd.h --- a/src/dhcpcd.h Sat Sep 12 15:58:03 2020 +0100 +++ b/src/dhcpcd.h Sat Sep 12 20:14:47 2020 +0100 @@ -117,8 +117,11 @@ struct dhcpcd_ctx { char pidfile[sizeof(PIDFILE) + IF_NAMESIZE + 1]; char vendor[256]; + bool stdin_valid; /* It's possible stdin, stdout and stderr */ + bool stdout_valid; /* could be closed when dhcpcd starts. */ + bool stderr_valid; + int stderr_fd; /* FD for logging to stderr */ int fork_fd; /* FD for the fork init signal pipe */ - int stderr_fd; /* FD for logging to stderr */ const char *cffile; unsigned long long options; char *logfile; diff -r fef58462dea6 -r 68ef863871d1 src/privsep.c --- a/src/privsep.c Sat Sep 12 15:58:03 2020 +0100 +++ b/src/privsep.c Sat Sep 12 20:14:47 2020 +0100 @@ -76,7 +76,6 @@ #ifdef HAVE_CAPSICUM #include #include -#define ps_rights_limit_stdio caph_limit_stdio #endif #ifdef HAVE_UTIL_H #include @@ -278,6 +277,25 @@ return -1; return 0; } + +static int +ps_rights_limit_stdio(struct dhcpcd_ctx *ctx) +{ + const int iebadf = CAPH_IGNORE_EBADF; + int error = 0; + + if (ctx->stdin_valid && + caph_limit_stream(STDIN_FILENO, CAPH_READ | iebadf) == -1) + error = -1; + if (ctx->stdout_valid && + caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | iebadf) == -1) + error = -1; + if (ctx->stderr_valid && + caph_limit_stream(STDERR_FILENO, CAPH_WRITE | iebadf) == -1) + error = -1; + + return error; +} #endif pid_t @@ -346,7 +364,7 @@ #ifdef PRIVSEP_RIGHTS /* We cannot limit the root process in any way. */ - if (ps_rights_limit_stdio() == -1) { + if (ps_rights_limit_stdio(ctx) == -1) { logerr("ps_rights_limit_stdio"); goto errexit; } @@ -484,7 +502,7 @@ if ((ctx->pf_inet_fd != -1 && ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1) || (ctx->link_fd != -1 && ps_rights_limit_fd(ctx->link_fd) == -1) || - ps_rights_limit_stdio() == -1) + ps_rights_limit_stdio(ctx) == -1) { logerr("%s: cap_rights_limit", __func__); return -1;