home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume22 / auth / part02 / authtcp.c < prev    next >
C/C++ Source or Header  |  1990-04-29  |  19KB  |  591 lines

  1. /*
  2. authtcp.c: Create a locally authenticated TCP connection.
  3. */
  4.  
  5. /* WARNING! WARNING! WARNING! */
  6. /* For the authentication to work, authtcp must run setuid auth! */
  7. /* All setuid programs are dangerous! Check them carefully! */
  8.  
  9. static char authtcpauthor[] =
  10. "authtcp was written by Daniel J. Bernstein.\n\
  11. Internet address: brnstnd@acf10.nyu.edu.\n";
  12.  
  13. static char authtcpversion[] = 
  14. "authtcp version 2.1, April 18, 1990.\n\
  15. Copyright (c) 1990, Daniel J. Bernstein.\n\
  16. All rights reserved.\n";
  17.  
  18. static char authtcpcopyright[] =
  19. "authtcp version 2.1, April 18, 1990.\n\
  20. Copyright (c) 1990, Daniel J. Bernstein.\n\
  21. All rights reserved.\n\
  22. \n\
  23. Until January 1, 1995, you are granted the following rights: A. To make\n\
  24. copies of this work in original form, so long as (1) the copies are exact\n\
  25. and complete; (2) the copies include the copyright notice, this paragraph,\n\
  26. and the disclaimer of warranty in their entirety. B. To distribute this\n\
  27. work, or copies made under the provisions above, so long as (1) this is\n\
  28. the original work and not a derivative form; (2) you do not charge a fee\n\
  29. for copying or for distribution; (3) you ensure that the distributed form\n\
  30. includes the copyright notice, this paragraph, and the disclaimer of\n\
  31. warranty in their entirety. These rights are temporary and revocable upon\n\
  32. written, oral, or other notice by Daniel J. Bernstein. These rights are\n\
  33. automatically revoked on January 1, 1995. This copyright notice shall be\n\
  34. governed by the laws of the state of New York.\n\
  35. \n\
  36. If you have questions about authtcp or about this copyright notice,\n\
  37. or if you would like additional rights beyond those granted above,\n\
  38. please feel free to contact the author at brnstnd@acf10.nyu.edu\n\
  39. on the Internet.\n";
  40.  
  41. static char authtcpwarranty[] =
  42. "To the extent permitted by applicable law, Daniel J. Bernstein disclaims\n\
  43. all warranties, explicit or implied, including but not limited to the\n\
  44. implied warranties of merchantability and fitness for a particular purpose.\n\
  45. Daniel J. Bernstein is not and shall not be liable for any damages,\n\
  46. incidental or consequential, arising from the use of this program, even\n\
  47. if you inform him of the possibility of such damages. This disclaimer\n\
  48. shall be governed by the laws of the state of New York.\n\
  49. \n\
  50. In other words, use this program at your own risk.\n\
  51. \n\
  52. If you have questions about authtcp or about this disclaimer of warranty,\n\
  53. please feel free to contact the author at brnstnd@acf10.nyu.edu\n\
  54. on the Internet.\n";
  55.  
  56. static char authtcpusage[] =
  57. "Usage: authtcp [ -dn ] [ -pport ] [ -xXvACHUVW ] inetaddr tcpport program \n\
  58. Help:  authtcp -H\n";
  59.  
  60. static char authtcphelp[] =
  61. "authtcp creates a locally authenticated TCP connection to an Internet host.\n\
  62. \n\
  63. authtcp -A: print authorship notice\n\
  64. authtcp -C: print copyright notice\n\
  65. authtcp -H: print this notice\n\
  66. authtcp -U: print short usage summary\n\
  67. authtcp -V: print version number\n\
  68. authtcp -W: print disclaimer of warranty\n\
  69. \n\
  70. authtcp [ -dn ] [ -pport ] [ -vxXrR ] inetaddr tcpport program:\n\
  71.         connect to tcpport at inetaddr and run program\n\
  72.   -dn: pass the connection to the program in file descriptor n (default 6)\n\
  73.   -pport: attempt to use a particular local port\n\
  74.   -v: verbose: proclaim success; report unusual program termination\n\
  75.   -x: authenticate by telling authd(8) about the connection (default)\n\
  76.   -X: do not locally authenticate\n\
  77.   -r: attempt to authenticate the remote side as well (default), placing\n\
  78.       user@host and TCP into environment variables REMOTE and PROTO\n\
  79.   -R: do not remotely authenticate\n\
  80. \n\
  81. If you have questions about or suggestions for authtcp, please feel free\n\
  82. to contact the author, Daniel J. Bernstein, at brnstnd@acf10.nyu.edu\n\
  83. on the Internet.\n";
  84.  
  85. #include <stdio.h>
  86. #include <sys/types.h>
  87. #include <sys/socket.h>
  88. #include <netinet/in.h>
  89. #include <arpa/inet.h>
  90. #include <netdb.h>
  91. #include <errno.h>
  92. extern int errno;
  93. extern char *malloc(); /* many systems don't have malloc.h */
  94. #include <pwd.h>
  95. #include <sys/file.h>
  96. #ifdef BSD
  97. #include <limits.h>
  98. #endif
  99. #include <sys/wait.h>
  100. #include <sys/time.h>
  101. #include <sys/resource.h>
  102. #include <signal.h>
  103. #include <ctype.h>
  104. extern int getopt();
  105. extern char *optarg; /* these should be in getopt.h! */
  106. extern int optind;
  107. #include "authuser.h"
  108. #include "djberr.h"
  109. #include "djbatoi.h"
  110.  
  111. #ifndef AUTHDIR
  112. #define AUTHDIR "/usr/etc/auth"
  113. #endif
  114.  
  115. #ifndef MAXHOSTNAMELEN
  116. #define MAXHOSTNAMELEN 128 /* stupid Suns don't define this in sys/param.h */
  117. #endif
  118.  
  119. int flagcont = 0;
  120.  
  121. sigcont()
  122. {
  123.  flagcont = 1;
  124. }
  125.  
  126. int flagauth = 1;
  127. int flagremote = 1;
  128. int filedesc = 6;
  129. unsigned short localport = 0;
  130.  
  131. char *strinetaddr = NULL;
  132. char *strtcpport = NULL;
  133. char **program = NULL;
  134.  
  135. main(argc,argv,envp)
  136. int argc;
  137. char *argv[];
  138. char *envp[];
  139. {
  140.  extern char **environ;
  141.  int opt;
  142.  struct sockaddr_in sa;
  143.  unsigned long in;
  144.  unsigned short remoteport;
  145.  int s;
  146.  int t;
  147.  int uid;
  148.  int euid;
  149.  int dummy;
  150.  char authfn[sizeof(AUTHDIR) + 30];
  151.  int authfd;
  152.  char lockfn[sizeof(AUTHDIR) + 30];
  153.  int lockfd;
  154.  char lockbuf[32]; /* 5 pid, 1 :, 10 I, 1 ., 5 R, 1 \n, 8 U, 1\0 */
  155.  struct passwd *pw;
  156.  char strfd[20];
  157.  int flagverbose = 0;
  158.  struct hostent *he;
  159.  struct servent *se;
  160.  union wait status;
  161.  int f;
  162.  struct in_addr inet; /* dummy for inet_ntoa() */
  163.  int flagsigintign;
  164.  int flagsigquitign;
  165.  int flagsigtstpign;
  166.  int flagsighupign;
  167.  int flagsigalrmign;
  168.  int flagsigxcpuign;
  169.  int flagsigxfszign;
  170.  int flagsigvtalrmign;
  171.  int flagsigprofign;
  172.  
  173.  /* ALERT! ALERT! ALERT! We're probably running setuid auth! */
  174.  /* Note that accounting is by real uid, not effective uid. */
  175.  /* The system should be careful about setuid core dumps 'n' such. */
  176.  
  177.  uid = getuid();
  178.  euid = geteuid();
  179.  
  180.  /* The following are necessary to be absolutely sure of removing the
  181.     authentication entry. It's a flaw of the signal handling system that
  182.     every new extension could turn a secure program like this into an
  183.     (ever so slightly) insecure one. */
  184.  flagsigintign = (signal(SIGINT,SIG_IGN) == SIG_IGN);
  185.  flagsigquitign = (signal(SIGQUIT,SIG_IGN) == SIG_IGN);
  186.  flagsigtstpign = (signal(SIGTSTP,SIG_IGN) == SIG_IGN);
  187.  flagsighupign = (signal(SIGHUP,SIG_IGN) == SIG_IGN);
  188.  flagsigalrmign = (signal(SIGALRM,SIG_IGN) == SIG_IGN);
  189.  flagsigxcpuign = (signal(SIGXCPU,SIG_IGN) == SIG_IGN);
  190.  flagsigxfszign = (signal(SIGXFSZ,SIG_IGN) == SIG_IGN);
  191.  flagsigvtalrmign = (signal(SIGVTALRM,SIG_IGN) == SIG_IGN);
  192.  flagsigprofign = (signal(SIGPROF,SIG_IGN) == SIG_IGN);
  193.  /* At least we can depend on SIG_IGN and SIG_DFL being the only
  194.     possible handlers passed through an exec. Programmers should note
  195.     the above trick to avoid having to worry about the signal() type. */
  196.  
  197.  while ((opt = getopt(argc,argv,"rRxXd:p:vACHUVW")) != EOF)
  198.    switch(opt)
  199.     {
  200.      case 'r': flagremote = 1; break;
  201.      case 'R': flagremote = 0; break;
  202.      case 'x': flagauth = 1; break;
  203.      case 'X': flagauth = 0; break;
  204.      case 'd': filedesc = atoi(optarg); break;
  205.      case 'p': localport = atoi(optarg); break;
  206.      case 'v': flagverbose = 1; break;
  207.      case 'A': (void) err(authtcpauthor); (void) setreuid(uid,uid); exit(1);
  208.      case 'C': (void) err(authtcpcopyright); (void) setreuid(uid,uid); exit(1);
  209.      case 'H': (void) err(authtcphelp); (void) setreuid(uid,uid); exit(1);
  210.      case 'U': (void) err(authtcpusage); (void) setreuid(uid,uid); exit(1);
  211.      case 'V': (void) err(authtcpversion); (void) setreuid(uid,uid); exit(1);
  212.      case 'W': (void) err(authtcpwarranty); (void) setreuid(uid,uid); exit(1);
  213.      case '?': (void) err(authtcpusage); (void) setreuid(uid,uid); exit(1);
  214.     }
  215.  argv += optind, argc -= optind;
  216.  while (*argv)
  217.   {
  218.    if (strinetaddr == NULL)
  219.      strinetaddr = *argv;
  220.    else if (strtcpport == NULL)
  221.      strtcpport = *argv;
  222.    else
  223.     {
  224.      program = argv;
  225.      break;
  226.     }
  227.    argv++;
  228.   }
  229.  
  230.  if ((program == NULL) || (*program == NULL))
  231.   {
  232.    err(authtcpusage); (void) setreuid(uid,uid); exit(1);
  233.    /* what else can you say? */
  234.   }
  235.  
  236.  t = strlen(strinetaddr) - 1;
  237.  if (isascii(strinetaddr[t]) && isdigit(strinetaddr[t]))
  238.    in = inet_addr(strinetaddr);
  239.  else
  240.    if ((he = gethostbyname(strinetaddr)) == NULL)
  241.      in = (unsigned long) -1;
  242.    else
  243.      in = *((unsigned long *) (he->h_addr));
  244.  
  245.  if (in == (unsigned long) -1)
  246.   {
  247.    errn2("authtcp: fatal: do not understand inetaddr %s",strinetaddr);
  248.    (void) setreuid(uid,uid);
  249.    exit(1);
  250.   }
  251.  
  252.  t = strlen(strtcpport) - 1;
  253.  if (isascii(strtcpport[t]) && isdigit(strtcpport[t]))
  254.    remoteport = atoi(strtcpport); /* so who cares if it's zero? */
  255.  else
  256.    if ((se = getservbyname(strtcpport,"tcp")) == NULL)
  257.      remoteport = 0;
  258.    else
  259.      remoteport = ntohs(se->s_port); /* inconsistency alert! */
  260.                      /* se->s_port is int! */
  261.  
  262.  /* We will depend on the type of localport and remoteport, namely */
  263.  /* unsigned short, to keep them between 0 and 65535. */
  264.  
  265.  /* The parent program must remain active and setuid auth, to wait for */
  266.  /* the child to finish and to remove its authentication entry. But the */
  267.  /* TCP connection must be made as the user---don't want fake rlogins! */
  268.  
  269.  /* We now switch to the real user ID. */
  270.  
  271.  if (setreuid(euid,uid))
  272.   {
  273.    perrn2("%s","authtcp: fatal: cannot setreuid");
  274.    (void) setreuid(uid,uid);
  275.    exit(1);
  276.   }
  277.  
  278.  if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) /* no security problem */
  279.   {
  280.    perrn2("%s","authtcp: fatal: cannot create socket");
  281.    (void) setreuid(uid,uid);
  282.    exit(1);
  283.   }
  284.  
  285.  /* The bind() below used to be if (localport) only. However, connect() */
  286.  /* can take arbitrarily long, there's no solution like select-accept, */
  287.  /* and authd should not have to wait to get its information. So we have */
  288.  /* to find our local port now. This is documented to work in PS1:8-31. */
  289.  
  290.  sa.sin_family = AF_INET;
  291.  sa.sin_port = htons(localport); /* ever seen a client do this before? */
  292.  sa.sin_addr.s_addr = INADDR_ANY; /* or this? */
  293.  
  294.  if (bind(s,&sa,sizeof(sa)) == -1)
  295.    if (localport)
  296.     {
  297.      perrn2("authtcp: fatal: cannot bind local port %u",
  298.             (unsigned int) localport);
  299.      (void) setreuid(uid,uid);
  300.      exit(1);
  301.     }
  302.    else
  303.     {
  304.      perrn2("%s","authtcp: fatal: cannot bind local port");
  305.      (void) setreuid(uid,uid);
  306.      exit(1);
  307.     }
  308.  
  309.  /* We now switch back to auth... */
  310.  if (setreuid(uid,euid))
  311.   {
  312.    perrn2("%s","authtcp: fatal: cannot setreuid");
  313.    (void) setreuid(uid,uid);
  314.    exit(1);
  315.   }
  316.  
  317.  if (flagauth)
  318.   {
  319.    dummy = sizeof(sa);
  320.    if ((pw = getpwuid(uid)) == NULL)
  321.     {
  322.      errn("authtcp: fatal: cannot authenticate: who are you?");
  323.      (void) setreuid(uid,uid);
  324.      exit(1);
  325.     }
  326.    if (getsockname(s,&sa,&dummy) == -1)
  327.     {
  328.      perrn2("%s","authtcp: fatal: cannot get socket name");
  329.      (void) setreuid(uid,uid);
  330.      exit(1);
  331.     }
  332.    (void) sprintf(authfn,"%s/tcp/%D.%u.%u",AUTHDIR,in,
  333.           (unsigned int) ntohs(sa.sin_port),(unsigned int) remoteport);
  334.    (void) sprintf(lockfn,"%s/tcp/lock.%u",AUTHDIR,
  335.           (unsigned int) ntohs(sa.sin_port));
  336.    if (((lockfd = open(lockfn,O_WRONLY | O_CREAT | O_EXCL,0600)) == -1)
  337.      &&(((lockfd = open(lockfn,O_RDONLY)) == -1)
  338.     ||(flock(lockfd,LOCK_EX) == -1)
  339.         ||(read(lockfd,lockbuf,31) <= 0)
  340.     ||((lockbuf[0] != '!')
  341.           &&((atoi(lockbuf) <= 0)
  342.              ||(kill(atoi(lockbuf),0) == 0))) /* okay, screw the last process */
  343.     ||(close(lockfd) == -1) /* impossible */
  344.         ||((lockfd = open(lockfn,O_WRONLY | O_CREAT | O_TRUNC,0600)) == -1)))
  345.     { /* yikes, that was incomprehensible */
  346.      errn2("authtcp: fatal: local port %u locked",
  347.        (unsigned int) ntohs(sa.sin_port));
  348.      (void) setreuid(uid,uid);
  349.      exit(1);
  350.     }
  351.    (void) flock(lockfd,LOCK_EX);
  352.    (void) sprintf(lockbuf,"%d:%D.%u\n%s",getpid(),in,(unsigned int) remoteport,
  353.               pw->pw_name);
  354.    (void) write(lockfd,lockbuf,strlen(lockbuf));
  355.    (void) flock(lockfd,LOCK_UN);
  356.   }
  357.  /* It isn't a disaster if the user kills us now; it's just a possible */
  358.  /* denial of service for later auths on this port. The amount of effort */
  359.  /* necessary to guarantee that denial is huge. */
  360.  
  361.  /* We now switch back to real... */
  362.  if (setreuid(euid,uid))
  363.   {
  364.    perrn2("%s","authtcp: fatal: cannot setreuid");
  365.    (void) setreuid(uid,euid);
  366.    if (flagauth)
  367.      (void) unlink(lockfn);
  368.    (void) setreuid(uid,uid);
  369.    exit(1);
  370.   }
  371.  
  372.  sa.sin_family = AF_INET;
  373.  sa.sin_port = htons(remoteport);
  374.  sa.sin_addr.s_addr = in;
  375.  
  376.  if (connect(s,&sa,sizeof(sa)) == -1)
  377.   {
  378.    perrn2("authtcp: fatal: cannot connect to %s",strinetaddr);
  379.    (void) setreuid(uid,euid);
  380.    if (flagauth)
  381.      (void) unlink(lockfn);
  382.    (void) setreuid(uid,uid);
  383.    exit(1);
  384.   }
  385.  
  386.  if (flagverbose)
  387.   {
  388.    inet.s_addr = in;
  389.    fprintf(stderr,"authtcp: connected to %s port %u\n",
  390.        inet_ntoa(inet),(unsigned int) remoteport);
  391.   }
  392.  
  393.  /* We now have the uids switched, the connection open in socket s. */
  394.  
  395.  /* We're about to write the authentication entry. We must remove it */
  396.  /* before coming back to the real uid and/or exiting. On the other */
  397.  /* hand, we must fork as the real uid: one system stupidity is that */
  398.  /* fork() uses the effective uid for MAXUPRC checks. Hence we fork */
  399.  /* as the real (i.e. switched) uid, before authentication. In fact, */
  400.  /* we delay authentication until it's convenient in the parent; the */
  401.  /* child program could run before the authentication file exists. */
  402.  /* This is not a race condition, for reasons explained in dir.doc. */
  403.  
  404.  if ((f = fork()) == 0) /* child */
  405.   {
  406.    if (setreuid(uid,uid))
  407.     {
  408.      perrn2("%s","authtcp: fatal: cannot setreuid");
  409.      exit(1);
  410.     }
  411.    if (filedesc)
  412.     {
  413.      if (dup2(s,filedesc) == -1)
  414.       {
  415.        perrn2("%s","authtcp: fatal: cannot use file descriptor");
  416.        exit(1);
  417.       }
  418.     }
  419.    else
  420.     {
  421.      (void) sprintf(strfd,"%d",s);
  422.      while (*(++argv))
  423.        if ((**argv == '=') && (*(*argv + 1) == '\0'))
  424.     {
  425.      *argv = strfd;
  426.      break;
  427.     }
  428.      /* don't need to complain about failure here */
  429.     }
  430.  
  431.    if (flagremote)
  432.     {
  433.      unsigned long in; /* keep confirming variables separate */
  434.      unsigned short local;
  435.      unsigned short remote;
  436.      char *ruser;
  437.      char *srem;
  438.      char **t;
  439.      char **trem;
  440.      char **tproto;
  441.      char **envbak;
  442.  
  443.      if (auth_fd(s,&in,&local,&remote) == -1)
  444.       {
  445.        perrn2("%s","authtcp: fatal: cannot confirm connection");
  446.        exit(1);
  447.       }
  448.      if ((ruser = auth_tcpuser(in,local,remote)) == NULL)
  449.        ruser = ""; /* bummer */
  450.      if ((srem = malloc(strlen(ruser) + 30)) == NULL)
  451.       {
  452.        perrn2("%s","authtcp: fatal: cannot allocate environment");
  453.        exit(1);
  454.       }
  455.      inet.s_addr = in;
  456.      sprintf(srem,"REMOTE=%s@%s",ruser,inet_ntoa(inet));
  457.      for (trem = envp;*trem;trem++)
  458.        if (strncmp(*trem,"REMOTE=",7) == 0)
  459.          break;
  460.      for (tproto = envp;*tproto;tproto++)
  461.        if (strncmp(*tproto,"PROTO=",6) == 0)
  462.          break;
  463.      if (!(*trem && *tproto))
  464.       {
  465.        envbak = envp;
  466.        if ((environ = (char **) malloc((trem - envp + 3) * sizeof(char *)))
  467.        == NULL)
  468.         {
  469.          perrn2("%s","authtcp: fatal: cannot allocate environment");
  470.          exit(1);
  471.         }
  472.        for (t = envbak;*t;t++)
  473.          environ[t - envbak] = *t; /* not worth a bcopy */
  474.        trem = environ + ((*trem ? trem : t++) - envbak);
  475.        tproto = environ + ((*tproto ? tproto : t++) - envbak);
  476.        environ[t - envbak] = NULL;
  477.       }
  478.      *trem = srem;
  479.      *tproto = "PROTO=TCP";
  480.      /* XXXXXX: Should we do confirming sanity checks here? */
  481.     }
  482.  
  483.    (void) signal(SIGINT,flagsigintign ? SIG_IGN : SIG_DFL);
  484.    (void) signal(SIGQUIT,flagsigquitign ? SIG_IGN : SIG_DFL);
  485.    (void) signal(SIGTSTP,flagsigtstpign ? SIG_IGN : SIG_DFL);
  486.    (void) signal(SIGHUP,flagsighupign ? SIG_IGN : SIG_DFL);
  487.    (void) signal(SIGALRM,flagsigalrmign ? SIG_IGN : SIG_DFL);
  488.    (void) signal(SIGXCPU,flagsigxcpuign ? SIG_IGN : SIG_DFL);
  489.    (void) signal(SIGXFSZ,flagsigxfszign ? SIG_IGN : SIG_DFL);
  490.    (void) signal(SIGVTALRM,flagsigvtalrmign ? SIG_IGN : SIG_DFL);
  491.    (void) signal(SIGPROF,flagsigprofign ? SIG_IGN : SIG_DFL);
  492.  
  493.    /* Yes, Virginia, this is portable. Read execvp(3). */
  494.    /* Annoying that there isn't a better interface, though. */
  495.    (void) execvp(*program,program); /* must use environ! */
  496.    perrn2("authtcp: fatal: cannot execute %s",*program);
  497.    exit(1);
  498.   }
  499.  /* no need to test for failing fork */
  500.  
  501.  if (setreuid(uid,euid))
  502.   {
  503.    perrn2("%s","authtcp: fatal: cannot setreuid");
  504.    (void) setreuid(uid,euid);
  505.    if (flagauth)
  506.      (void) unlink(lockfn);
  507.    (void) setreuid(uid,uid);
  508.    exit(1);
  509.   }
  510.  /* Now we're back to setuid auth in the parent, as we will remain. */
  511.  
  512.  if (flagauth)
  513.   {
  514.    /* authfn and pw are put together up above */
  515.    if ((authfd = open(authfn,O_WRONLY | O_CREAT | O_EXCL,0600)) == -1)
  516.     {
  517.      perrn2("%s","authtcp: fatal: cannot authenticate");
  518.      (void) setreuid(uid,euid);
  519.      if (flagauth)
  520.        (void) unlink(lockfn);
  521.      (void) setreuid(uid,uid);
  522.      exit(1);
  523.     }
  524.    (void) write(authfd,pw->pw_name,strlen(pw->pw_name));
  525.    (void) close(authfd); /* if it fails, tough luck. */
  526.   }
  527.  
  528.  (void) signal(SIGCONT,sigcont);
  529.  
  530.  /* Unless the user is auth we won't receive any signals. So the */
  531.  /* following handlers are mainly a convenience on machines where */
  532.  /* authtcp is not installed by the sysadmin. */
  533.  (void) signal(SIGTERM,SIG_IGN); /* kill child, not us! */
  534.  (void) signal(SIGTTOU,SIG_DFL); /* for stopping */
  535.  (void) signal(SIGTTIN,SIG_DFL); /* for stopping */
  536.  
  537.  /* We're going to leave the socket open, to completely close a possible */
  538.  /* security hole. The application must not depend upon the connection */
  539.  /* disappearing when it's closed. */
  540.  
  541.  for (t = getdtablesize();t >= 0;t--)
  542.    if ((s != t) && (t != 2)) /* have to leave stderr open! */
  543.      (void) close(t); /* might as well do a bit of dissociation */
  544.  
  545.  while (wait3(&status,WUNTRACED,(struct rusage *) NULL) >= 0)
  546.   {
  547.    if (status.w_stopval == WSTOPPED) /* if child stops, we stop */
  548.     {
  549.      flagcont = 0;
  550.      (void) signal(SIGTSTP,SIG_DFL); /* for stopping */
  551.      switch(status.w_stopsig)
  552.       { /* this just looks good */
  553.        case SIGSTOP: (void) kill(getpid(),SIGSTOP); break;
  554.        case SIGTSTP: (void) kill(getpid(),SIGTSTP); break;
  555.        case SIGTTOU: (void) kill(getpid(),SIGTTOU); break;
  556.        case SIGTTIN: (void) kill(getpid(),SIGTTIN); break;
  557.        default: (void) kill(getpid(),SIGSTOP); break;
  558.       }
  559.      while (flagcont == 0)
  560.        ; /* rack up CPU time waiting for the CONT---is there a better way? */
  561.      (void) signal(SIGTSTP,SIG_IGN);
  562.      (void) kill(f,SIGCONT); /* when we start, child starts */
  563.      continue;
  564.     }
  565.    if (flagverbose)
  566.      if (WIFSIGNALED(status))
  567.        if (status.w_coredump)
  568.     {
  569.      errn2("authtcp: fatal: killed by signal %d (core dumped)",
  570.                status.w_termsig);
  571.     }
  572.        else
  573.     {
  574.      errn2("authtcp: fatal: killed by signal %d",status.w_termsig);
  575.     }
  576.    break;
  577.   }
  578.  
  579.  if (flagauth)
  580.   {
  581.    if (unlink(authfn) == -1) /* had better succeed! */
  582.     {
  583.      perrn2("authtcp: fatal: cannot unlink authentication entry %s",authfn);
  584.      exit(1);
  585.     }
  586.    (void) unlink(lockfn);
  587.   }
  588.  
  589.  exit(WIFEXITED(status) ? ((int) status.w_retcode) : 1);
  590. }
  591.