* pty_paranoia.c: New file; do many paranoid checks about ctty
authorTom Yu <tlyu@mit.edu>
Fri, 11 May 2001 03:01:46 +0000 (03:01 +0000)
committerTom Yu <tlyu@mit.edu>
Fri, 11 May 2001 03:01:46 +0000 (03:01 +0000)
handling by the pty drivers.

* Makefile.in: Add rules for pty_paranoia and check-paranoia,
which runs pty_paranoia.

* configure.in: Define REVOKE_NEEDS_OPEN for Tru64.  Add support
for program building and run flags for the sake of pty_paranoia.

* open_slave.c: Fix somewhat; AIX doesn't like opening the ctty
twice, so only do initial open if we special-case it in
configure.in, e.g. for Tru64.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@13239 dc483132-0cff-0310-8789-dd5450dbe970

src/util/pty/ChangeLog
src/util/pty/Makefile.in
src/util/pty/configure.in
src/util/pty/open_slave.c
src/util/pty/pty_paranoia.c [new file with mode: 0644]

index c17a9e5b0596d724f1c84517164ca5756205fc44..eda91e588322819fd5c35eab3e042dc810c61c63 100644 (file)
@@ -1,3 +1,18 @@
+2001-05-10  Tom Yu  <tlyu@mit.edu>
+
+       * pty_paranoia.c: New file; do many paranoid checks about ctty
+       handling by the pty drivers.
+
+       * Makefile.in: Add rules for pty_paranoia and check-paranoia,
+       which runs pty_paranoia.
+
+       * configure.in: Define REVOKE_NEEDS_OPEN for Tru64.  Add support
+       for program building and run flags for the sake of pty_paranoia.
+
+       * open_slave.c: Fix somewhat; AIX doesn't like opening the ctty
+       twice, so only do initial open if we special-case it in
+       configure.in, e.g. for Tru64.
+
 2001-05-08  Tom Yu  <tlyu@mit.edu>
 
        * logwtmp.c: Delete code under "#if 0".  Fix reversed test for
index 83d61dc63a82f79b5ff740255d92be37fc374390..32687108b5c168a2a47b4bf81f0320291e048c15 100644 (file)
@@ -6,6 +6,10 @@ RELDIR=../util/pty
 
 SED = sed
 
+KRB5_RUN_ENV= @KRB5_RUN_ENV@
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+
 LIB=pty
 LIBMAJOR=1
 LIBMINOR=1
@@ -49,6 +53,12 @@ dump-utmp: dump-utmp.o
        $(CC) $(LDFLAGS) -o dump-utmp dump-utmp.o
 dump-utmp.o: dump-utmp.c
 
+pty_paranoia: pty_paranoia.o $(COM_ERR_DEPLIB) $(PTY_DEPLIB)
+       $(CC_LINK) -o pty_paranoia pty_paranoia.o $(PTY_LIB) $(COM_ERR_LIB) $(LIBS)
+
+check-paranoia: pty_paranoia
+       $(KRB5_RUN_ENV) ./pty_paranoia
+
 install-unix:: install-libs
 
 clean-unix::
index 8dce229026644bcffbea82aed095510082f75cee..480532211d257eb3d8f37953fe5dadc3a2caeefe 100644 (file)
@@ -35,6 +35,8 @@ alpha*-dec-osf*)
                AC_DEFINE(HAVE_SETLUID)
                LOGINLIBS="$LOGINLIBS -lsecurity"
        )
+       AC_MSG_RESULT(will open ctty prior to revoke due to OSF/1 lossage)
+       AC_DEFINE(REVOKE_NEEDS_OPEN)
        ;;
 *-*-solaris*)
      AC_DEFINE(PUSH_PTEM)
@@ -257,4 +259,6 @@ KRB5_AC_INET6
 AC_C_CONST
 KRB5_BUILD_LIBRARY_WITH_DEPS
 KRB5_BUILD_LIBOBJS
+KRB5_BUILD_PROGRAM
+KRB5_RUN_FLAGS
 V5_AC_OUTPUT_MAKEFILE
index 6e80befb2df10de83d7eebba150ff60cdcb4584a..cc52228b8cfc4991978d9f9fd8681b86f86fa18f 100644 (file)
@@ -39,8 +39,8 @@ pty_open_slave(const char *slave, int *fd)
     ptyint_void_association();
 
     /*
-     * Make a first attempt at acquiring the ctty.  This is necessary
-     * for several reasons:
+     * Make a first attempt at acquiring the ctty under certain
+     * condisions.  This is necessary for several reasons:
      *
      * Under Irix, if you open a pty slave and then close it, a
      * subsequent open of the slave will cause the master to read EOF.
@@ -52,11 +52,13 @@ pty_open_slave(const char *slave, int *fd)
      *
      * Anyway, sshd seems to make a practice of doing this.
      */
+#if defined(VHANG_FIRST) || defined(REVOKE_NEEDS_OPEN)
     retval = pty_open_ctty(slave, fd);
     if (retval)
        return retval;
     if (*fd < 0)
        return PTY_OPEN_SLAVE_OPENFAIL;
+#endif
 
     /* chmod and chown the slave. */
     if (chmod(slave, 0))
@@ -76,7 +78,9 @@ pty_open_slave(const char *slave, int *fd)
 
     /* Open the pty for real. */
     retval = pty_open_ctty(slave, &tmpfd);
+#if defined(VHANG_FIRST) || defined(REVOKE_NEEDS_OPEN)
     close(*fd);
+#endif
     if (retval) {
        *fd = -1;
        return PTY_OPEN_SLAVE_OPENFAIL;
diff --git a/src/util/pty/pty_paranoia.c b/src/util/pty/pty_paranoia.c
new file mode 100644 (file)
index 0000000..e07e0ff
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2001 by the Massachusetts Institute of Technology.
+ * 
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of
+ * M.I.T. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability
+ * of this software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ */
+
+/*
+ * This bears some explanation.
+ *
+ * There are multiple child processes and a parent process.  These
+ * communicate via pipes (which we assume here to be unidirectional).
+ * The pipes are:
+ *
+ * pp1 - parent -> any children
+ *
+ * p1p - any children -> parent
+ *
+ * p21 - only child2 -> child1
+ *
+ * A parent process will acquire a pty master and slave via
+ * pty_getpty().  It will then fork a process, child1.  It then does a
+ * waitpid() for child1, and then writes to child2 via syncpipe pp1.
+ * It then reads from child3 via syncpipe p1p, then closes the
+ * master.  It writes to child3 via syncpipe pp1 to indicate that it
+ * has closed the master.  It then reads from child3 via syncpipe p1p
+ * and exits with a value appropriate to what it read from child3.
+ *
+ * child1 will acquire the slave as its ctty and fork child2; child1
+ * will exit once it reads from the syncpipe p21 from child2.
+ *
+ * child2 will set a signal handler for SIGHUP and then write to
+ * child1 via syncpipe p21 to indicate that child2 has set up the
+ * handler.  It will then read from the syncpipe pp1 from the parent
+ * to confirm that the parent has seen child1 exit, and then checks to
+ * see if it still has a ctty.  Under Unix98, and likely earlier
+ * System V derivatives, the exiting of the session leader associated
+ * with a ctty (in this case, child1) will cause the entire session to
+ * lose its ctty.
+ *
+ * child2 will then check to see if it can reopen the slave, and
+ * whether it has a ctty after reopening it.  This should fail on most
+ * systems.
+ *
+ * child2 will then fork child3 and immediately exit.
+ *
+ * child3 will write to the syncpipe p1p and read from the syncpipe
+ * pp1.  It will then check if it has a ctty and then attempt to
+ * reopen the slave.  This should fail.  It will then write to the
+ * parent via syncpipe p1p and exit.
+ */
+
+#include <com_err.h>
+#include "libpty.h"
+#include "pty-int.h"
+#include <sys/wait.h>
+#include <stdlib.h>
+
+char *prog;
+int masterfd, slavefd;
+char slave[64], slave2[64];
+pid_t pid1, pid2, pid3;
+int status1, status2;
+int pp1[2], p1p[2], p21[2];
+
+void handler(int);
+void rdsync(int, int *, const char *);
+void wrsync(int, int, const char *);
+void testctty(const char *);
+void child1(void);
+void child2(void);
+void child3(void);
+
+void
+handler(int sig)
+{
+    printf("pid %ld got signal %d\n", (long)getpid(), sig);
+    fflush(stdout);
+    return;
+}
+
+void
+rdsync(int fd, int *status, const char *caller)
+{
+    int n;
+    char c;
+
+    while ((n = read(fd, &c, 1)) < 0) {
+       if (errno != EINTR) {
+           fprintf(stderr, "wrsync: %s", caller);
+           perror("");
+           exit(1);
+       } else {
+           printf("rdsync: %s: got EINTR; looping\n", caller);
+           fflush(stdout);
+       }
+    }
+    if (!n) {
+       fprintf(stderr, "rdsync: %s: unexpected EOF\n", caller);
+       exit(1);
+    }
+    printf("rdsync: %s: got sync byte\n", caller);
+    fflush(stdout);
+    if (status != NULL)
+       *status = c;
+}
+
+void
+wrsync(int fd, int status, const char *caller)
+{
+    int n;
+    char c;
+
+    c = status;
+    while ((n = write(fd, &c, 1)) < 0) {
+       if (errno != EINTR) {
+           fprintf(stderr, "wrsync: %s", caller);
+           perror("");
+           exit(1);
+       } else {
+           printf("wrsync: %s: got EINTR; looping\n", caller);
+           fflush(stdout);
+       }
+    }
+#if 0
+    printf("wrsync: %s: sent sync byte\n", caller);
+#endif
+    fflush(stdout);
+}
+
+void
+testctty(const char *caller)
+{
+    int fd;
+
+    fd = open("/dev/tty", O_RDWR);
+    if (fd < 0) {
+       printf("%s: no ctty\n", caller);
+    } else {
+       printf("%s: have ctty\n", caller);
+    }
+}
+
+void
+child3(void)
+{
+
+    ptyint_void_association();
+    slavefd = open(slave, O_RDWR);
+    if (slavefd < 0) {
+       printf("child3: failed reopen of slave\n");
+       fflush(stdout);
+       exit(0);
+    }
+#ifdef TIOCSTTY
+    ioctl(slavefd, TIOCSTTY, 0);
+#endif
+
+    printf("child3: reopened slave\n");
+    testctty("child3: after reopen of slave");
+    close(slavefd);
+    testctty("child3: after close of slave");
+
+    /*
+     * Sync for parent to close master.
+     */
+    wrsync(p1p[1], 0, "child3->parent");
+    rdsync(pp1[0], NULL, "parent->child3");
+
+    testctty("child3: after close of master");
+    slavefd = open(slave, O_RDWR);
+    if (slavefd < 0) {
+       printf("child3: failed reopen of slave after master close "
+              "errno=%ld (%s)\n", (long)errno, strerror(errno));
+       wrsync(p1p[1], 0, "child3->parent");
+       fflush(stdout);
+       exit(0);
+    }
+    printf("child3: reopened slave after master close\n");
+    testctty("child3: after reopen of slave after master close\n");
+    wrsync(p1p[1], 1, "child3->parent");
+    fflush(stdout);
+    exit(0);
+}
+
+void
+child2(void)
+{
+    struct sigaction sa;
+
+    close(p21[0]);
+    setpgid(0, 0);
+    sa.sa_flags = 0;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_handler = handler;
+    if (sigaction(SIGHUP, &sa, NULL) < 0) {
+       perror("child2: sigaction");
+       fflush(stdout);
+       exit(1);
+    }
+    printf("child2: set up signal handler\n");
+    testctty("child2: after start");
+    wrsync(p21[1], 0, "child2->child1");
+    rdsync(pp1[0], NULL, "parent->child2");
+    testctty("child2: after child1 exit");
+    close(slavefd);
+    testctty("child2: after close of slavefd");
+    slavefd = open(slave, O_RDWR);
+    if (slavefd < 0) {
+       printf("child2: failed reopen of slave\n");
+       fflush(stdout);
+       exit(0);
+    }
+    printf("child2: reopened slave\n");
+    testctty("child2: after reopen of slave");
+    fflush(stdout);
+    close(slavefd);
+    pid3 = fork();
+    if (!pid3) {
+       child3();
+    } else if (pid3 == -1) {
+       perror("child2: fork of child3");
+       exit(1);
+    }
+    printf("child2: forked child3=%ld\n", (long)pid3);
+    fflush(stdout);
+    exit(0);
+
+}
+
+void
+child1(void)
+{
+
+    close(pp1[1]);
+    close(p1p[0]);
+    close(masterfd);
+    ptyint_void_association();
+    slavefd = open(slave, O_RDWR);
+    if (slavefd < 0) {
+       perror("child1: open slave");
+       exit(1);
+    }
+#ifdef TIOCSTTY
+    ioctl(slavefd, TIOCSTTY, 0);
+#endif
+
+    printf("child1: opened slave\n");
+    testctty("child1: after slave open");
+
+    if (pipe(p21) < 0) {
+       perror("pipe child2->child1");
+       exit(1);
+    }
+    pid2 = fork();
+    if (!pid2) {
+       child2();
+    } else if (pid2 == -1) {
+       perror("child1: fork child2");
+       exit(1);
+    }
+    close(p21[1]);
+    printf("child1: forked child2=%ld\n", (long)pid2);
+    fflush(stdout);
+    rdsync(p21[0], NULL, "child2->child1");
+    exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+    long retval;
+    int status;
+
+    prog = argv[0];
+
+    retval = pty_getpty(&masterfd, slave, sizeof(slave));
+
+    if (retval) {
+       com_err(prog, retval, "open master");
+       exit(1);
+    }
+    printf("parent: master opened; slave=%s\n", slave);
+    fflush(stdout);
+
+    if (pipe(pp1) < 0) {
+       perror("pipe parent->child1");
+       exit(1);
+    }
+    if (pipe(p1p) < 0) {
+       perror("pipe child1->parent");
+       exit(1);
+    }
+
+    pid1 = fork();
+    if (!pid1) {
+       child1();
+    } else if (pid1 == -1) {
+       perror("fork of child1");
+       exit(1);
+    }
+    printf("parent: forked child1=%ld\n", (long)pid1);
+    fflush(stdout);
+    if (waitpid(pid1, &status1, 0) < 0) {
+       perror("waitpid for child1");
+       exit(1);
+    }
+    printf("parent: child1 exited, status=%d\n", status1);
+    wrsync(pp1[1], 0, "parent->child2");
+    rdsync(p1p[0], NULL, "child3->parent");
+    printf("parent: closing master\n");
+    fflush(stdout);
+    close(masterfd);
+    printf("parent: closed master\n");
+    wrsync(pp1[1], 0, "parent->child3");
+    rdsync(p1p[0], &status, "child3->parent");
+    if (status) {
+       fprintf(stderr, "got status %d\n", status);
+    }
+    exit(status);
+}