From: Paul Mackerras Date: Sat, 6 Nov 2004 05:42:29 +0000 (+0000) Subject: Extend safe_fork to include the shuffling of fds to get the X-Git-Tag: ppp-2.4.7~200 X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=6b5081d8af903c5a7e3c4f30cd6c4afd9f5b658f Extend safe_fork to include the shuffling of fds to get the ones we want on fds 0, 1, 2. --- diff --git a/pppd/main.c b/pppd/main.c index ee86eb8..25076ca 100644 --- a/pppd/main.c +++ b/pppd/main.c @@ -66,7 +66,7 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define RCSID "$Id: main.c,v 1.142 2004/11/04 10:05:23 paulus Exp $" +#define RCSID "$Id: main.c,v 1.143 2004/11/06 05:42:29 paulus Exp $" #include #include @@ -873,12 +873,8 @@ detach() void reopen_log() { -#ifdef ULTRIX - openlog("pppd", LOG_PID); -#else openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); setlogmask(LOG_UPTO(LOG_INFO)); -#endif } /* @@ -1521,34 +1517,83 @@ bad_signal(sig) * safe_fork - Create a child process. The child closes all the * file descriptors that we don't want to leak to a script. * The parent waits for the child to do this before returning. + * This also arranges for the specified fds to be dup'd to + * fds 0, 1, 2 in the child. */ pid_t -safe_fork() +safe_fork(int infd, int outfd, int errfd) { pid_t pid; - int pipefd[2]; + int fd, pipefd[2]; char buf[1]; + /* make sure fds 0, 1, 2 are occupied (probably not necessary) */ + while ((fd = dup(fd_devnull)) >= 0) { + if (fd > 2) { + close(fd); + break; + } + } + if (pipe(pipefd) == -1) pipefd[0] = pipefd[1] = -1; pid = fork(); - if (pid < 0) + if (pid < 0) { + error("fork failed: %m"); return -1; + } if (pid > 0) { + /* parent */ close(pipefd[1]); /* this read() blocks until the close(pipefd[1]) below */ complete_read(pipefd[0], buf, 1); close(pipefd[0]); return pid; } + + /* Executing in the child */ sys_close(); #ifdef USE_TDB tdb_close(pppdb); #endif + + /* make sure infd, outfd and errfd won't get tromped on below */ + if (infd == 1 || infd == 2) + infd = dup(infd); + if (outfd == 0 || outfd == 2) + outfd = dup(outfd); + if (errfd == 0 || errfd == 1) + errfd = dup(errfd); + + /* dup the in, out, err fds to 0, 1, 2 */ + if (infd != 0) + dup2(infd, 0); + if (outfd != 1) + dup2(outfd, 1); + if (errfd != 2) + dup2(errfd, 2); + + closelog(); + if (log_to_fd > 2) + close(log_to_fd); + if (the_channel->close) + (*the_channel->close)(); + else + close(devfd); /* some plugins don't have a close function */ + close(fd_ppp); + close(fd_devnull); + if (infd != 0) + close(infd); + if (outfd != 1) + close(outfd); + if (errfd != 2) + close(errfd); + notify(fork_notifier, 0); close(pipefd[0]); /* this close unblocks the read() call above in the parent */ close(pipefd[1]); + return 0; } @@ -1566,10 +1611,17 @@ device_script(program, in, out, dont_wait) int pid; int status = -1; int errfd; - int fd; + + if (log_to_fd >= 0) + errfd = log_to_fd; + else + errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600); ++conn_running; - pid = safe_fork(); + pid = safe_fork(in, out, errfd); + + if (pid != 0 && log_to_fd < 0) + close(errfd); if (pid < 0) { --conn_running; @@ -1594,59 +1646,14 @@ device_script(program, in, out, dont_wait) /* here we are executing in the child */ - /* make sure fds 0, 1, 2 are occupied */ - while ((fd = dup(in)) >= 0) { - if (fd > 2) { - close(fd); - break; - } - } - - /* dup in and out to fds > 2 */ - { - int fd1 = in, fd2 = out, fd3 = log_to_fd; - - in = dup(in); - out = dup(out); - if (log_to_fd >= 0) { - errfd = dup(log_to_fd); - } else { - errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600); - } - close(fd1); - close(fd2); - close(fd3); - } - - /* close fds 0 - 2 and any others we can think of */ - close(0); - close(1); - close(2); - if (the_channel->close) - (*the_channel->close)(); - else - close(devfd); /* some plugins don't have a close function */ - closelog(); - close(fd_devnull); - - /* dup the in, out, err fds to 0, 1, 2 */ - dup2(in, 0); - close(in); - dup2(out, 1); - close(out); - if (errfd >= 0) { - dup2(errfd, 2); - close(errfd); - } - + setgid(getgid()); setuid(uid); if (getuid() != uid) { - error("setuid failed"); + fprintf(stderr, "pppd: setuid failed\n"); exit(1); } - setgid(getgid()); execl("/bin/sh", "sh", "-c", program, (char *)0); - error("could not exec /bin/sh: %m"); + perror("pppd: could not exec /bin/sh"); exit(99); /* NOTREACHED */ } @@ -1687,7 +1694,7 @@ run_program(prog, args, must_exist, done, arg) return 0; } - pid = safe_fork(); + pid = safe_fork(fd_devnull, fd_devnull, fd_devnull); if (pid == -1) { error("Failed to create child process for %s: %m", prog); return -1; @@ -1706,25 +1713,12 @@ run_program(prog, args, must_exist, done, arg) setuid(0); /* set real UID = root */ setgid(getegid()); - /* Ensure that nothing of our device environment is inherited. */ - closelog(); - if (the_channel->close) - (*the_channel->close)(); - - /* Don't pass handles to the PPP device, even by accident. */ - dup2(fd_devnull, 0); - dup2(fd_devnull, 1); - dup2(fd_devnull, 2); - close(fd_devnull); - #ifdef BSD /* Force the priority back to zero if pppd is running higher. */ if (setpriority (PRIO_PROCESS, 0, 0) < 0) warn("can't reset priority to 0: %m"); #endif - /* SysV recommends a second fork at this point. */ - /* run the program */ execve(prog, args, script_env); if (must_exist || errno != ENOENT) { diff --git a/pppd/pppd.h b/pppd/pppd.h index 232aa32..f4f5381 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -39,7 +39,7 @@ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * $Id: pppd.h,v 1.85 2004/11/04 09:46:50 paulus Exp $ + * $Id: pppd.h,v 1.86 2004/11/06 05:42:29 paulus Exp $ */ /* @@ -243,6 +243,7 @@ extern int error_count; /* # of times error() has been called */ extern char ppp_devnam[MAXPATHLEN]; extern char remote_number[MAXNAMELEN]; /* Remote telephone number, if avail. */ extern int ppp_session_number; /* Session number (eg PPPoE session) */ +extern int fd_devnull; /* fd open to /dev/null */ extern int listen_time; /* time to listen first (ms) */ extern struct notifier *pidchange; /* for notifications of pid changing */ @@ -465,7 +466,7 @@ void timeout __P((void (*func)(void *), void *arg, int s, int us)); void untimeout __P((void (*func)(void *), void *arg)); /* Cancel call to func(arg) */ void record_child __P((int, char *, void (*) (void *), void *)); -pid_t safe_fork __P((void)); /* Fork & close stuff in child */ +pid_t safe_fork __P((int, int, int)); /* Fork & close stuff in child */ int device_script __P((char *cmd, int in, int out, int dont_wait)); /* Run `cmd' with given stdin and stdout */ pid_t run_program __P((char *prog, char **args, int must_exist, diff --git a/pppd/tty.c b/pppd/tty.c index 5d65cd2..30d4765 100644 --- a/pppd/tty.c +++ b/pppd/tty.c @@ -68,7 +68,7 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define RCSID "$Id: tty.c,v 1.18 2004/11/04 10:02:26 paulus Exp $" +#define RCSID "$Id: tty.c,v 1.19 2004/11/06 05:42:29 paulus Exp $" #include #include @@ -535,7 +535,7 @@ int connect_tty() status = EXIT_LOCK_FAILED; if (lockflag && !privopen) { if (lock(devnam) < 0) - return -1; + goto errret; locked = 1; } @@ -558,11 +558,11 @@ int connect_tty() prio = privopen? OPRIO_ROOT: tty_options[0].priority; if (prio < OPRIO_ROOT) seteuid(uid); - ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); + real_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0); err = errno; if (prio < OPRIO_ROOT) seteuid(0); - if (ttyfd >= 0) + if (real_ttyfd >= 0) break; errno = err; if (err != EINTR) { @@ -570,9 +570,9 @@ int connect_tty() status = EXIT_OPEN_FAILED; } if (!persist || err != EINTR) - return -1; + goto errret; } - real_ttyfd = ttyfd; + ttyfd = real_ttyfd; if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1 || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) warn("Couldn't reset non-blocking mode on device: %m"); @@ -629,30 +629,32 @@ int connect_tty() close(opipe[0]); close(opipe[1]); if (!ok) - return -1; + goto errret; } else { if (device_script(ptycommand, pty_master, pty_master, 1) < 0) - return -1; - ttyfd = pty_slave; - close(pty_master); - pty_master = -1; + goto errret; } } else if (pty_socket != NULL) { int fd = open_socket(pty_socket); if (fd < 0) - return -1; + goto errret; if (!start_charshunt(fd, fd)) - return -1; + goto errret; + close(fd); } else if (notty) { if (!start_charshunt(0, 1)) - return -1; + goto errret; } else if (record_file != NULL) { - if (!start_charshunt(ttyfd, ttyfd)) - return -1; + int fd = dup(ttyfd); + if (!start_charshunt(fd, fd)) + goto errret; } - if (using_pty || record_file != NULL) + if (using_pty || record_file != NULL) { ttyfd = pty_slave; + close(pty_master); + pty_master = -1; + } /* run connection script */ if ((connector && connector[0]) || initializer) { @@ -669,11 +671,11 @@ int connect_tty() if (device_script(initializer, ttyfd, ttyfd, 0) < 0) { error("Initializer script failed"); status = EXIT_INIT_FAILED; - return -1; + goto errret; } if (kill_link) { disconnect_tty(); - return -1; + goto errret; } info("Serial port initialized."); } @@ -682,11 +684,11 @@ int connect_tty() if (device_script(connector, ttyfd, ttyfd, 0) < 0) { error("Connect script failed"); status = EXIT_CONNECT_FAILED; - return -1; + goto errret; } if (kill_link) { disconnect_tty(); - return -1; + goto errret; } info("Serial connection established."); } @@ -711,7 +713,7 @@ int connect_tty() status = EXIT_OPEN_FAILED; } if (!persist || errno != EINTR || hungup || kill_link) - return -1; + goto errret; } close(i); } @@ -734,6 +736,22 @@ int connect_tty() listen_time = connect_delay; return ttyfd; + + errret: + if (pty_master >= 0) { + close(pty_master); + pty_master = -1; + } + if (pty_slave >= 0) { + close(pty_slave); + pty_slave = -1; + } + if (real_ttyfd >= 0) { + close(real_ttyfd); + real_ttyfd = -1; + } + ttyfd = -1; + return -1; } @@ -752,8 +770,6 @@ void disconnect_tty() void tty_close_fds() { - if (pty_master >= 0) - close(pty_master); if (pty_slave >= 0) close(pty_slave); if (real_ttyfd >= 0) { @@ -893,27 +909,27 @@ start_charshunt(ifd, ofd) { int cpid; - cpid = safe_fork(); + cpid = safe_fork(ifd, ofd, (log_to_fd >= 0? log_to_fd: 2)); if (cpid == -1) { error("Can't fork process for character shunt: %m"); return 0; } if (cpid == 0) { /* child */ - close(pty_slave); + reopen_log(); + if (!nodetach) + log_to_fd = -1; + else if (log_to_fd >= 0) + log_to_fd = 2; + setgid(getgid()); setuid(uid); if (getuid() != uid) fatal("setuid failed"); - setgid(getgid()); - if (!nodetach) - log_to_fd = -1; - charshunt(ifd, ofd, record_file); + charshunt(0, 1, record_file); exit(0); } charshunt_pid = cpid; add_notifier(&sigreceived, stop_charshunt, 0); - close(pty_master); - pty_master = -1; record_child(cpid, "pppd (charshunt)", charshunt_done, NULL); return 1; } @@ -1110,9 +1126,6 @@ charshunt(ifd, ofd, record_file) } else if (nibuf == 0) { /* end of file from stdin */ stdin_readable = 0; - /* do a 0-length write, hopefully this will generate - an EOF (hangup) on the slave side. */ - write(pty_master, inpacket_buf, 0); if (recordf) if (!record_write(recordf, 4, NULL, 0, &lasttime)) recordf = NULL; @@ -1149,7 +1162,8 @@ charshunt(ifd, ofd, record_file) if (!record_write(recordf, 1, obufp, nobuf, &lasttime)) recordf = NULL; } - } + } else if (!stdin_readable) + pty_readable = 0; if (FD_ISSET(ofd, &writey)) { n = nobuf; if (olevel + n > max_level)