Extend safe_fork to include the shuffling of fds to get the
authorPaul Mackerras <paulus@samba.org>
Sat, 6 Nov 2004 05:42:29 +0000 (05:42 +0000)
committerPaul Mackerras <paulus@samba.org>
Sat, 6 Nov 2004 05:42:29 +0000 (05:42 +0000)
ones we want on fds 0, 1, 2.

pppd/main.c
pppd/pppd.h
pppd/tty.c

index ee86eb8f35c0e84ec67b1d3f61be8a7b6d70a81d..25076ca50d3aa59bfdc4a8b26264aeb8a764dd4c 100644 (file)
@@ -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 <stdio.h>
 #include <ctype.h>
@@ -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) {
index 232aa32ae4e36130605bbe1854647ec2b0e9b2a0..f4f53817e1e3f7464c61c3cb74a3d4a28019ce4c 100644 (file)
@@ -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,
index 5d65cd2765a485f43a07a3d99dcef81b47ddc851..30d476559f6c32a3604f5495d90a4a54ac9ab005 100644 (file)
@@ -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 <stdio.h>
 #include <ctype.h>
@@ -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)