Subject:  v22i002:  RFC931 TCP Authentication server, Part02/02
Newsgroups: comp.sources.unix
Approved: rsalz@uunet.UU.NET
X-Checksum-Snefru: 18bc6a4c 9c051008 1e57a113 534ecf48

Submitted-by: Dan Bernstein <brnstnd@acf10.nyu.edu>
Posting-number: Volume 22, Issue 2
Archive-name: auth2.1/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  attachport.c authtcp.c rfc931
# Wrapped by rsalz@litchi.bbn.com on Mon Apr 30 15:53:40 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'attachport.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'attachport.c'\"
else
echo shar: Extracting \"'attachport.c'\" \(20921 characters\)
sed "s/^X//" >'attachport.c' <<'END_OF_FILE'
X/*
Xattachport.c: attach a server program to a TCP port
X*/
X
X/* WARNING! WARNING! WARNING! */
X/* For the authentication to work, attachport must run setuid auth! */
X/* All setuid programs are dangerous! Check them carefully! */
X
Xstatic char attachportauthor[] =
X"attachport was written by Daniel J. Bernstein.\n\
XInternet address: brnstnd@acf10.nyu.edu.\n";
X
Xstatic char attachportversion[] = 
X"attachport version 4.1, April 18, 1990.\n\
XCopyright (c) 1990, Daniel J. Bernstein.\n\
XAll rights reserved.\n";
X
Xstatic char attachportcopyright[] =
X"attachport version 4.1, April 18, 1990.\n\
XCopyright (c) 1990, Daniel J. Bernstein.\n\
XAll rights reserved.\n\
X\n\
XUntil January 1, 1995, you are granted the following rights: A. To make\n\
Xcopies of this work in original form, so long as (1) the copies are exact\n\
Xand complete; (2) the copies include the copyright notice, this paragraph,\n\
Xand the disclaimer of warranty in their entirety. B. To distribute this\n\
Xwork, or copies made under the provisions above, so long as (1) this is\n\
Xthe original work and not a derivative form; (2) you do not charge a fee\n\
Xfor copying or for distribution; (3) you ensure that the distributed form\n\
Xincludes the copyright notice, this paragraph, and the disclaimer of\n\
Xwarranty in their entirety. These rights are temporary and revocable upon\n\
Xwritten, oral, or other notice by Daniel J. Bernstein. These rights are\n\
Xautomatically revoked on January 1, 1995. This copyright notice shall be\n\
Xgoverned by the laws of the state of New York.\n\
X\n\
XIf you have questions about attachport or about this copyright notice,\n\
Xor if you would like additional rights beyond those granted above,\n\
Xplease feel free to contact the author at brnstnd@acf10.nyu.edu\n\
Xon the Internet.\n";
X
Xstatic char attachportwarranty[] =
X"To the extent permitted by applicable law, Daniel J. Bernstein disclaims\n\
Xall warranties, explicit or implied, including but not limited to the\n\
Ximplied warranties of merchantability and fitness for a particular purpose.\n\
XDaniel J. Bernstein is not and shall not be liable for any damages,\n\
Xincidental or consequential, arising from the use of this program, even\n\
Xif you inform him of the possibility of such damages. This disclaimer\n\
Xshall be governed by the laws of the state of New York.\n\
X\n\
XIn other words, use this program at your own risk.\n\
X\n\
XIf you have questions about attachport or about this disclaimer of warranty,\n\
Xplease feel free to contact the author at brnstnd@acf10.nyu.edu\n\
Xon the Internet.\n";
X
Xstatic char attachportusage[] =
X"Usage: attachport [ -01vrRxXACHUVW ] [ -pport ] program [ arg ... ]\n\
XHelp:  attachport -H\n";
X
Xstatic char attachporthelp[] =
X"attachport attaches a server program to a TCP port.\n\
X\n\
Xattachport -A: print authorship notice\n\
Xattachport -C: print copyright notice\n\
Xattachport -H: print this notice\n\
Xattachport -U: print short usage summary\n\
Xattachport -V: print version number\n\
Xattachport -W: print disclaimer of warranty\n\
X\n\
Xattachport [ -01vrRxX ] [ -pport ] program [ arg ... ]: attach program to port\n\
X  -v: verbose: proclaim success\n\
X  -1: print port number on standard output\n\
X  -0: check every ten seconds for fd 0 to have links; if none, wither away\n\
X  -x: locally authenticate connections with authd(8) (default)\n\
X  -X: do not locally authenticate\n\
X  -r: attempt to authenticate the remote side as well (default), placing\n\
X      user@host and TCP into environment variables REMOTE and PROTO\n\
X  -R: do not remotely authenticate\n\
X  -pport: attach server to a particular TCP port\n\
X\n\
XIf you have questions about or suggestions for attachport, please feel free\n\
Xto contact the author, Daniel J. Bernstein, at brnstnd@acf10.nyu.edu\n\
Xon the Internet.\n";
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/file.h>
X#ifdef BSD
X#include <limits.h>
X#endif
X#include <sys/ioctl.h>
X#include <sys/socket.h>
X#include <sys/time.h>
X#include <sys/wait.h>
X#include <sys/resource.h>
X#include <sys/stat.h>
X#include <netinet/in.h>
X#include <arpa/inet.h>
X#include <netdb.h>
X#include <signal.h>
X#include <sys/param.h>
X#include <pwd.h>
Xextern char *malloc(); /* many systems don't have malloc.h */
Xextern int getopt();
Xextern char *optarg; /* these should be in getopt.h! */
Xextern int optind;
X#include <ctype.h>
X#include "authuser.h"
X#include "djberr.h"
X#include "djbatoi.h"
X
X#ifndef AUTHDIR
X#define AUTHDIR "/usr/etc/auth"
X#endif
X
X#ifndef MAXHOSTNAMELEN
X#define MAXHOSTNAMELEN 128 /* stupid Suns don't define this in sys/param.h */
X#endif
X
Xint numkids = 0;
Xint flagauth = 1;
X
Xunsigned long myinetaddr()
X{
X char hn[MAXHOSTNAMELEN + 1];
X struct hostent *he;
X
X if (gethostname(hn,MAXHOSTNAMELEN) == -1)
X   return((unsigned long) -1);
X if ((he = gethostbyname(hn)) == NULL)
X   return((unsigned long) -1);
X
X return (*((unsigned long *) (he->h_addr)));
X}
X
Xdissociatetty()
X{
X int fd;
X
X if ((fd = open("/dev/tty",O_RDWR)) == -1)
X  {
X   perrn2("%s","attachport: warning: cannot open /dev/tty");
X  }
X else
X  {
X   if (ioctl(fd,(unsigned long) TIOCNOTTY,(char *) NULL) == -1)
X     perrn2("%s","attachport: warning: cannot dissociate /dev/tty");
X   (void) close(fd);
X  }
X}
X
Xint uid;
Xint euid;
X
Xint flagcheckin = 0;
Xint alrmcounter = 0;
X
Xint flagdie;
X
Xsigterm()
X{
X flagdie = 1;
X}
X
Xsigalrm()
X{
X struct stat st;
X
X if (flagcheckin)
X  {
X   (void) fstat(0,&st);
X   if (st.st_nlink == 0)
X     sigterm();
X  }
X if ((++alrmcounter) % 12) /* every two minutes */
X  {
X   alrmcounter = 0;
X   (void) kill(getpid(),SIGCHLD);
X  }
X}
X
Xsigchld()
X{
X int w;
X char authfn[sizeof(AUTHDIR) + 30];
X char authpfn[sizeof(AUTHDIR) + 30];
X int authpfd;
X int r;
X int noweuid = geteuid();
X
X if (noweuid == uid) /* oopsie */
X   if (setreuid(uid,euid))
X    {
X     perrn2("%s","attachport: warning: cannot setreuid");
X     return; /* This is impossible anyway. */
X    }
X
X while ((w = wait3((union wait *) 0,WNOHANG,(struct rusage *) NULL)) > 0)
X  {
X   numkids--;
X   if (flagauth)
X    {
X     (void) sprintf(authpfn,"%s/tcp/ps.%d.%d",AUTHDIR,getpid(),w);
X     if ((authpfd = open(authpfn,O_RDONLY,0600)) == -1)
X      {
X       perrn2("attachport: warning: cannot unlink authentication entry %s",authpfn);
X       continue;
X      }
X     r = read(authpfd,authfn,sizeof(authfn));
X     (void) close(authpfd);
X     if ((r <= 0) || (strncmp(authfn,AUTHDIR,strlen(AUTHDIR))))
X      { /* Make sure the worst damage we can do is confined to AUTHDIR. */
X       perrn2("attachport: warning: cannot unlink authentication entry %s",authpfn);
X       continue;
X      }
X     authfn[r] = '\0';
X     if (unlink(authfn) == -1) /* had better succeed! */
X      {
X       perrn2("attachport: warning: cannot unlink authentication entry %s",authfn);
X       continue;
X      }
X     if (unlink(authpfn) == -1) /* had better succeed! */
X      {
X       perrn2("attachport: warning: cannot unlink authentication entry %s",authpfn);
X      }
X    }
X  }
X
X if (noweuid == uid) /* daisie */
X   if (setreuid(euid,uid))
X    {
X     perrn2("%s","attachport: warning: cannot setreuid");
X     return;
X    }
X}
X
Xextern char **environ;
X
Xmain(argc,argv,envp)
Xint argc;
Xchar *argv[];
Xchar *envp[];
X{
X int opt;
X int flagverbose = 0;
X int flagminiverb = 0;
X int flagremote = 2;
X char *strlocalport = "0";
X unsigned short localport;
X char **program = NULL;
X struct sockaddr_in sa;
X int s;
X int t;
X unsigned long in;
X int dummy;
X struct passwd *pw;
X int f;
X int authfd;
X int authpfd;
X char authfn[sizeof(AUTHDIR) + 30];
X char authpfn[sizeof(AUTHDIR) + 30];
X char lockfn[sizeof(AUTHDIR) + 30];
X int lockfd;
X char lockbuf[32]; /* 5 pid, 1 :, 10 I, 1 ., 5 R, 1 \n, 8 U, 1\0 */
X		   /* we use just 5 pid, 1 -, 8 U, 1\0 */
X int lockbuflen;
X char foobuf[32];
X struct itimerval it;
X fd_set ready;
X struct servent *se;
X struct in_addr inet; /* dummy for inet_ntoa */
X int flagsigintign;
X int flagsigquitign;
X int flagsigtstpign;
X int flagsighupign;
X int flagsigxcpuign;
X int flagsigxfszign;
X int flagsigvtalrmign;
X int flagsigprofign;
X int flagsigchldign;
X int flagsigalrmign;
X int flagsigtermign;
X
X /* ALERT! ALERT! ALERT! We're probably running setuid auth! */
X /* Note that accounting is by real uid, not effective uid. */
X /* The system should be careful about setuid core dumps 'n' such. */
X
X uid = getuid();
X euid = geteuid();
X
X /* The following are necessary to be absolutely sure of removing the
X    authentication entry. It's a flaw of the signal handling system that
X    every new extension could turn a secure program like this into an
X    (ever so slightly) insecure one. */
X flagsigintign = (signal(SIGINT,SIG_IGN) == SIG_IGN);
X flagsigquitign = (signal(SIGQUIT,SIG_IGN) == SIG_IGN);
X flagsigtstpign = (signal(SIGTSTP,SIG_IGN) == SIG_IGN);
X flagsighupign = (signal(SIGHUP,SIG_IGN) == SIG_IGN);
X flagsigxcpuign = (signal(SIGXCPU,SIG_IGN) == SIG_IGN);
X flagsigxfszign = (signal(SIGXFSZ,SIG_IGN) == SIG_IGN);
X flagsigvtalrmign = (signal(SIGVTALRM,SIG_IGN) == SIG_IGN);
X flagsigprofign = (signal(SIGPROF,SIG_IGN) == SIG_IGN);
X flagsigchldign = (signal(SIGCHLD,SIG_IGN) == SIG_IGN);
X flagsigalrmign = (signal(SIGALRM,SIG_IGN) == SIG_IGN);
X flagsigtermign = (signal(SIGTERM,SIG_IGN) == SIG_IGN);
X /* At least we can depend on SIG_IGN and SIG_DFL being the only
X    possible handlers passed through an exec. Programmers should note
X    the above trick to avoid having to worry about the signal() type. */
X
X while ((opt = getopt(argc,argv,"01vrRxXp:ACHUVW")) != EOF)
X   switch(opt)
X    {
X     case 'v': flagverbose = 1; break;
X     case '1': flagminiverb = 1; break;
X     case '0': flagcheckin = 1; break;
X     case 'r': flagremote = 1; break;
X     case 'R': flagremote = 0; break;
X     case 'x': flagauth = 1; break;
X     case 'X': flagauth = 0; break;
X     case 'p': strlocalport = optarg; break;
X     case 'A': (void) err(attachportauthor);(void)setreuid(uid,uid); exit(1);
X     case 'C': (void) err(attachportcopyright);(void)setreuid(uid,uid); exit(1);
X     case 'H': (void) err(attachporthelp);(void)setreuid(uid,uid); exit(1);
X     case 'U': (void) err(attachportusage);(void)setreuid(uid,uid); exit(1);
X     case 'V': (void) err(attachportversion);(void)setreuid(uid,uid); exit(1);
X     case 'W': (void) err(attachportwarranty);(void)setreuid(uid,uid); exit(1);
X     case '?': (void) err(attachportusage);(void)setreuid(uid,uid); exit(1);
X    }
X argv += optind; argc -= optind;
X program = argv;
X
X if ((program == NULL) || (*program == NULL))
X  {
X   (void) err(attachportusage);
X   (void) setreuid(uid,uid); exit(1);
X  }
X
X in = myinetaddr();
X if (in == (unsigned long) -1)
X  {
X   (void) errn("attachport: fatal: can't find my own Internet number?!");
X   (void) setreuid(uid,uid); exit(1);
X  }
X
X t = strlen(strlocalport) - 1;
X if (isascii(strlocalport[t]) && isdigit(strlocalport[t]))
X   localport = atoi(strlocalport); /* so who cares if it's zero? */
X else
X   if ((se = getservbyname(strlocalport,"tcp")) == NULL)
X     localport = 0;
X   else
X     localport = ntohs(se->s_port); /* inconsistency alert! */
X				    /* se->s_port is int! */
X
X if (flagauth)
X   if ((pw = getpwuid(uid)) == NULL)
X    {
X     (void) errn("attachport: fatal: cannot authenticate: who are you?");
X     (void) setreuid(uid,uid); exit(1);
X    }
X
X /* We now switch to the real user id, though preserving euid for auth. */
X
X if (setreuid(euid,uid))
X  {
X   perrn2("%s","attachport: fatal: cannot setreuid");
X   (void) setreuid(uid,uid); exit(1);
X  }
X
X if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) /* no security problem */
X  {
X   perrn2("%s","attachport: fatal: cannot create socket");
X   (void) setreuid(uid,uid); exit(1);
X  }
X
X sa.sin_family = AF_INET;
X sa.sin_port = htons(localport);
X sa.sin_addr.s_addr = INADDR_ANY;
X
X if (bind(s,&sa,sizeof(sa)) == -1)
X  {
X   perrn2("%s","attachport: fatal: cannot bind");
X   (void) setreuid(uid,uid); exit(1);
X  }
X
X if (listen(s,5) == -1) /* 5 should be an option! */
X  {
X   perrn2("%s","attachport: fatal: cannot listen");
X   (void) setreuid(uid,uid); exit(1);
X  }
X
X dissociatetty();
X
X (void) signal(SIGTERM,sigterm); /* for killaport */
X (void) signal(SIGALRM,sigalrm); /* used to be just if flagcheckin */
X it.it_value.tv_sec = 10; it.it_value.tv_usec = 0; /* every ten seconds */
X it.it_interval.tv_sec = 10; it.it_interval.tv_usec = 0;
X (void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0);
X
X /* we still have uids switched */
X if (setreuid(uid,euid))
X  {
X   perrn2("%s","attachport: fatal: cannot setreuid");
X   (void) setreuid(uid,uid); exit(1);
X  }
X /* Now we're back to setuid auth... */
X
X dummy = sizeof(sa);
X if (getsockname(s,&sa,&dummy) == -1)
X  {
X   perrn2("%s","attachport: fatal: cannot get socket name");
X   (void) setreuid(uid,uid); exit(1);
X  }
X if (flagremote == 2)
X   flagremote = (ntohs(sa.sin_port) != 113);
X if (flagverbose)
X   (void) errn2("attachport: attached to port %d",ntohs(sa.sin_port));
X if (flagminiverb)
X  {
X   (void) printf("%d\n",ntohs(sa.sin_port));
X   (void) fflush(stdout);
X  }
X if (flagauth)
X  {
X   (void) sprintf(lockfn,"%s/tcp/lock.%u",AUTHDIR,
X		  (unsigned int) ntohs(sa.sin_port));
X   if (((lockfd = open(lockfn,O_WRONLY | O_CREAT | O_EXCL,0600)) == -1)
X     &&(((lockfd = open(lockfn,O_RDONLY)) == -1)
X	||(flock(lockfd,LOCK_EX) == -1)
X        ||(read(lockfd,lockbuf,31) <= 0)
X	||((lockbuf[0] != '!')
X          &&((atoi(lockbuf) <= 0)
X             ||(kill(atoi(lockbuf),0) == 0))) /* okay, screw the last process */
X	||(close(lockfd) == -1) /* impossible */
X        ||((lockfd = open(lockfn,O_WRONLY | O_CREAT | O_TRUNC,0600)) == -1)))
X    { /* yikes, that was incomprehensible */
X     errn2("attachport: fatal: local port %u locked",
X	   (unsigned int) ntohs(sa.sin_port));
X     (void) setreuid(uid,uid); exit(1);
X    }
X   (void) flock(lockfd,LOCK_EX);
X   (void) sprintf(lockbuf,"%d-%s",getpid(),pw->pw_name);
X   lockbuflen = strlen(lockbuf);
X   (void) sprintf(foobuf,"!%s%d",pw->pw_name,getpid());
X   (void) write(lockfd,lockbuf,lockbuflen);
X   (void) flock(lockfd,LOCK_UN);
X  }
X
X /* We must remain setuid auth as long as there are live authentication */
X /* entries. Otherwise the user could kill us and, if lucky enough, */
X /* misauthenticate future connections. It isn't so important to worry */
X /* about the lock file: that can only lead to a denial of service, and */
X /* it would take a huge amount of effort to guarantee that denial. */
X /* Anyway, we have to fork as the real uid: one system stupidity is */
X /* that fork() uses the effective uid for MAXUPRC checks. This isn't */
X /* a problem if there are no live authentication entries. */
X
X /* The solution is simple: We screw up the lock file while forking as */
X /* the real uid, then restore it afterwards. If we're killed in the */
X /* middle, authd will notice. This once again reduces the problem to */
X /* denial of service, which is acceptable. (In fact, the nature of the */
X /* messed up lock file is that it can't even cause denial of service.) */
X /* Ha! */
X
X (void) signal(SIGCHLD,sigchld);
X for (;;)
X   if (flagdie)
X     if (numkids > 0)
X       sleep(60);
X     else
X      {
X       if (flagauth)
X	 (void) unlink(lockfn);
X       (void) setreuid(uid,uid); exit(0); /* ahhh, so simple */
X      }
X   else
X    {
X     FD_ZERO(&ready); /* why, oh why doesn't this pass lint? */
X     FD_SET(s,&ready);
X     while ((select(s + 1,&ready,(fd_set *) 0,(fd_set *) 0,0) < 0)
X	    && !flagdie)
X       ; /* on error, ready won't be affected, so this is safe */
X     if (!flagdie) /* could be set any time, so we have to check */
X      {
X       dummy = sizeof(sa);
X       (void) flock(lockfd,LOCK_EX);
X       (void) lseek(lockfd,(off_t) 0,0);
X       (void) write(lockfd,foobuf,lockbuflen);
X       if ((t = accept(s,&sa,&dummy)) > -1)
X         if (setreuid(euid,uid))
X	  {
X	   perrn2("%s","attachport: warning: cannot setreuid");
X	   (void) setreuid(uid,euid);
X	  }
X         else if ((f = fork()) == 0)
X          {
X           if (setreuid(uid,euid))
X            {
X             perrn2("%s","attachport: fatal: cannot setreuid");
X             (void) setreuid(uid,uid); exit(1);
X            }
X           if (flagauth)
X            {
X             (void) sprintf(authfn,"%s/tcp/%D.%d.%d",AUTHDIR,
X                            sa.sin_addr.s_addr,localport,ntohs(sa.sin_port));
X             (void) sprintf(authpfn,"%s/tcp/ps.%d.%d",AUTHDIR,getppid(),getpid());
X  	     if ((authpfd = open(authpfn,O_WRONLY | O_CREAT | O_EXCL,0600)) == -1)
X  	      {
X               perrn2("%s","attachport: warning: cannot authenticate");
X	      }
X             if ((authfd = open(authfn,O_WRONLY | O_CREAT | O_EXCL,0600)) == -1)
X              {
X               perrn2("%s","attachport: warning: cannot authenticate");
X              }
X             (void) write(authpfd,authfn,strlen(authfn));
X             (void) write(authfd,pw->pw_name,strlen(pw->pw_name));
X             (void) close(authpfd);
X             (void) close(authfd); /* if it fails, tough luck. */
X            }
X
X           if (flagremote)
X            {
X             unsigned long in; /* keep confirming variables separate */
X             unsigned short local;
X             unsigned short remote;
X             char *ruser;
X             char *srem;
X             char **temp;
X             char **trem;
X             char **tproto;
X             char **envbak;
X        
X             if (auth_fd(t,&in,&local,&remote) == -1)
X              {
X               perrn2("%s","attachport: fatal: cannot confirm connection");
X               exit(1);
X              }
X             if ((ruser = auth_tcpuser(in,local,remote)) == NULL)
X               ruser = ""; /* bummer */
X             if ((srem = malloc(strlen(ruser) + 30)) == NULL)
X              {
X               perrn2("%s","attachport: fatal: cannot allocate environment");
X               exit(1);
X              }
X	     inet.s_addr = in;
X	     sprintf(srem,"REMOTE=%s@%s",ruser,inet_ntoa(inet));
X             for (trem = envp;*trem;trem++)
X               if (strncmp(*trem,"REMOTE=",7) == 0)
X                 break;
X             for (tproto = envp;*tproto;tproto++)
X               if (strncmp(*tproto,"PROTO=",6) == 0)
X                 break;
X             if (!(*trem && *tproto))
X              {
X               envbak = envp;
X               if ((environ = (char **) malloc((trem - envp + 3) * sizeof(char*)))
X		   == NULL)
X                {
X                 perrn2("%s","attachport: fatal: cannot allocate environment");
X                 exit(1);
X                }
X               for (temp = envbak;*temp;temp++)
X                 environ[temp - envbak] = *temp; /* not worth a bcopy */
X               trem = environ + ((*trem ? trem : temp++) - envbak);
X               tproto = environ + ((*tproto ? tproto : temp++) - envbak);
X               environ[temp - envbak] = NULL;
X              }
X             *trem = srem;
X             *tproto = "PROTO=TCP";
X             /* XXXXXX: Should we do confirming sanity checks here? */
X            }
X           (void) close(0); (void) dup(t);
X           (void) close(1); (void) dup(t);
X           (void) close(2); (void) dup(t);
X	   for (t = getdtablesize();t > 2;t--)
X             (void) close(t);
X	
X           (void) signal(SIGINT,flagsigintign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGQUIT,flagsigquitign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGTSTP,flagsigtstpign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGHUP,flagsighupign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGXCPU,flagsigxcpuign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGXFSZ,flagsigxfszign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGVTALRM,flagsigvtalrmign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGPROF,flagsigprofign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGCHLD,flagsigchldign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGALRM,flagsigalrmign ? SIG_IGN : SIG_DFL);
X           (void) signal(SIGTERM,flagsigtermign ? SIG_IGN : SIG_DFL);
X
X           if (setreuid(uid,uid))
X            {
X             perrn2("%s","attachport: fatal: cannot setreuid");
X             (void) setreuid(uid,uid); exit(1);
X            }
X
X           /* Yes, Virginia, this is portable. Read execvp(3). */
X           /* Annoying that there isn't a better interface, though. */
X           (void) execvp(*program,program); /* must use environ! */
X	   /* option to yell to remote end about failure? hmmm */
X           exit(1);
X          }
X         else if (f == -1)
X          {
X           /* option to yell to remote end before closing? perhaps */
X           (void) close(t); /* sigh */
X           if (setreuid(uid,euid))
X            {
X             perrn2("%s","attachport: warning: cannot setreuid");
X	     (void) setreuid(uid,euid);
X            }
X          }
X         else
X          {
X           if (setreuid(uid,euid))
X            {
X             perrn2("%s","attachport: warning: cannot setreuid");
X	     (void) setreuid(uid,euid);
X            }
X	   numkids++;
X           (void) close(t);
X          }
X       (void) lseek(lockfd,(off_t) 0,0);
X       (void) write(lockfd,lockbuf,lockbuflen);
X       (void) flock(lockfd,LOCK_UN);
X      }
X    }
X /*NOTREACHED*/
X}
END_OF_FILE
if test 20921 -ne `wc -c <'attachport.c'`; then
    echo shar: \"'attachport.c'\" unpacked with wrong size!
fi
# end of 'attachport.c'
fi
if test -f 'authtcp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'authtcp.c'\"
else
echo shar: Extracting \"'authtcp.c'\" \(19108 characters\)
sed "s/^X//" >'authtcp.c' <<'END_OF_FILE'
X/*
Xauthtcp.c: Create a locally authenticated TCP connection.
X*/
X
X/* WARNING! WARNING! WARNING! */
X/* For the authentication to work, authtcp must run setuid auth! */
X/* All setuid programs are dangerous! Check them carefully! */
X
Xstatic char authtcpauthor[] =
X"authtcp was written by Daniel J. Bernstein.\n\
XInternet address: brnstnd@acf10.nyu.edu.\n";
X
Xstatic char authtcpversion[] = 
X"authtcp version 2.1, April 18, 1990.\n\
XCopyright (c) 1990, Daniel J. Bernstein.\n\
XAll rights reserved.\n";
X
Xstatic char authtcpcopyright[] =
X"authtcp version 2.1, April 18, 1990.\n\
XCopyright (c) 1990, Daniel J. Bernstein.\n\
XAll rights reserved.\n\
X\n\
XUntil January 1, 1995, you are granted the following rights: A. To make\n\
Xcopies of this work in original form, so long as (1) the copies are exact\n\
Xand complete; (2) the copies include the copyright notice, this paragraph,\n\
Xand the disclaimer of warranty in their entirety. B. To distribute this\n\
Xwork, or copies made under the provisions above, so long as (1) this is\n\
Xthe original work and not a derivative form; (2) you do not charge a fee\n\
Xfor copying or for distribution; (3) you ensure that the distributed form\n\
Xincludes the copyright notice, this paragraph, and the disclaimer of\n\
Xwarranty in their entirety. These rights are temporary and revocable upon\n\
Xwritten, oral, or other notice by Daniel J. Bernstein. These rights are\n\
Xautomatically revoked on January 1, 1995. This copyright notice shall be\n\
Xgoverned by the laws of the state of New York.\n\
X\n\
XIf you have questions about authtcp or about this copyright notice,\n\
Xor if you would like additional rights beyond those granted above,\n\
Xplease feel free to contact the author at brnstnd@acf10.nyu.edu\n\
Xon the Internet.\n";
X
Xstatic char authtcpwarranty[] =
X"To the extent permitted by applicable law, Daniel J. Bernstein disclaims\n\
Xall warranties, explicit or implied, including but not limited to the\n\
Ximplied warranties of merchantability and fitness for a particular purpose.\n\
XDaniel J. Bernstein is not and shall not be liable for any damages,\n\
Xincidental or consequential, arising from the use of this program, even\n\
Xif you inform him of the possibility of such damages. This disclaimer\n\
Xshall be governed by the laws of the state of New York.\n\
X\n\
XIn other words, use this program at your own risk.\n\
X\n\
XIf you have questions about authtcp or about this disclaimer of warranty,\n\
Xplease feel free to contact the author at brnstnd@acf10.nyu.edu\n\
Xon the Internet.\n";
X
Xstatic char authtcpusage[] =
X"Usage: authtcp [ -dn ] [ -pport ] [ -xXvACHUVW ] inetaddr tcpport program \n\
XHelp:  authtcp -H\n";
X
Xstatic char authtcphelp[] =
X"authtcp creates a locally authenticated TCP connection to an Internet host.\n\
X\n\
Xauthtcp -A: print authorship notice\n\
Xauthtcp -C: print copyright notice\n\
Xauthtcp -H: print this notice\n\
Xauthtcp -U: print short usage summary\n\
Xauthtcp -V: print version number\n\
Xauthtcp -W: print disclaimer of warranty\n\
X\n\
Xauthtcp [ -dn ] [ -pport ] [ -vxXrR ] inetaddr tcpport program:\n\
X        connect to tcpport at inetaddr and run program\n\
X  -dn: pass the connection to the program in file descriptor n (default 6)\n\
X  -pport: attempt to use a particular local port\n\
X  -v: verbose: proclaim success; report unusual program termination\n\
X  -x: authenticate by telling authd(8) about the connection (default)\n\
X  -X: do not locally authenticate\n\
X  -r: attempt to authenticate the remote side as well (default), placing\n\
X      user@host and TCP into environment variables REMOTE and PROTO\n\
X  -R: do not remotely authenticate\n\
X\n\
XIf you have questions about or suggestions for authtcp, please feel free\n\
Xto contact the author, Daniel J. Bernstein, at brnstnd@acf10.nyu.edu\n\
Xon the Internet.\n";
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <arpa/inet.h>
X#include <netdb.h>
X#include <errno.h>
Xextern int errno;
Xextern char *malloc(); /* many systems don't have malloc.h */
X#include <pwd.h>
X#include <sys/file.h>
X#ifdef BSD
X#include <limits.h>
X#endif
X#include <sys/wait.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X#include <signal.h>
X#include <ctype.h>
Xextern int getopt();
Xextern char *optarg; /* these should be in getopt.h! */
Xextern int optind;
X#include "authuser.h"
X#include "djberr.h"
X#include "djbatoi.h"
X
X#ifndef AUTHDIR
X#define AUTHDIR "/usr/etc/auth"
X#endif
X
X#ifndef MAXHOSTNAMELEN
X#define MAXHOSTNAMELEN 128 /* stupid Suns don't define this in sys/param.h */
X#endif
X
Xint flagcont = 0;
X
Xsigcont()
X{
X flagcont = 1;
X}
X
Xint flagauth = 1;
Xint flagremote = 1;
Xint filedesc = 6;
Xunsigned short localport = 0;
X
Xchar *strinetaddr = NULL;
Xchar *strtcpport = NULL;
Xchar **program = NULL;
X
Xmain(argc,argv,envp)
Xint argc;
Xchar *argv[];
Xchar *envp[];
X{
X extern char **environ;
X int opt;
X struct sockaddr_in sa;
X unsigned long in;
X unsigned short remoteport;
X int s;
X int t;
X int uid;
X int euid;
X int dummy;
X char authfn[sizeof(AUTHDIR) + 30];
X int authfd;
X char lockfn[sizeof(AUTHDIR) + 30];
X int lockfd;
X char lockbuf[32]; /* 5 pid, 1 :, 10 I, 1 ., 5 R, 1 \n, 8 U, 1\0 */
X struct passwd *pw;
X char strfd[20];
X int flagverbose = 0;
X struct hostent *he;
X struct servent *se;
X union wait status;
X int f;
X struct in_addr inet; /* dummy for inet_ntoa() */
X int flagsigintign;
X int flagsigquitign;
X int flagsigtstpign;
X int flagsighupign;
X int flagsigalrmign;
X int flagsigxcpuign;
X int flagsigxfszign;
X int flagsigvtalrmign;
X int flagsigprofign;
X
X /* ALERT! ALERT! ALERT! We're probably running setuid auth! */
X /* Note that accounting is by real uid, not effective uid. */
X /* The system should be careful about setuid core dumps 'n' such. */
X
X uid = getuid();
X euid = geteuid();
X
X /* The following are necessary to be absolutely sure of removing the
X    authentication entry. It's a flaw of the signal handling system that
X    every new extension could turn a secure program like this into an
X    (ever so slightly) insecure one. */
X flagsigintign = (signal(SIGINT,SIG_IGN) == SIG_IGN);
X flagsigquitign = (signal(SIGQUIT,SIG_IGN) == SIG_IGN);
X flagsigtstpign = (signal(SIGTSTP,SIG_IGN) == SIG_IGN);
X flagsighupign = (signal(SIGHUP,SIG_IGN) == SIG_IGN);
X flagsigalrmign = (signal(SIGALRM,SIG_IGN) == SIG_IGN);
X flagsigxcpuign = (signal(SIGXCPU,SIG_IGN) == SIG_IGN);
X flagsigxfszign = (signal(SIGXFSZ,SIG_IGN) == SIG_IGN);
X flagsigvtalrmign = (signal(SIGVTALRM,SIG_IGN) == SIG_IGN);
X flagsigprofign = (signal(SIGPROF,SIG_IGN) == SIG_IGN);
X /* At least we can depend on SIG_IGN and SIG_DFL being the only
X    possible handlers passed through an exec. Programmers should note
X    the above trick to avoid having to worry about the signal() type. */
X
X while ((opt = getopt(argc,argv,"rRxXd:p:vACHUVW")) != EOF)
X   switch(opt)
X    {
X     case 'r': flagremote = 1; break;
X     case 'R': flagremote = 0; break;
X     case 'x': flagauth = 1; break;
X     case 'X': flagauth = 0; break;
X     case 'd': filedesc = atoi(optarg); break;
X     case 'p': localport = atoi(optarg); break;
X     case 'v': flagverbose = 1; break;
X     case 'A': (void) err(authtcpauthor); (void) setreuid(uid,uid); exit(1);
X     case 'C': (void) err(authtcpcopyright); (void) setreuid(uid,uid); exit(1);
X     case 'H': (void) err(authtcphelp); (void) setreuid(uid,uid); exit(1);
X     case 'U': (void) err(authtcpusage); (void) setreuid(uid,uid); exit(1);
X     case 'V': (void) err(authtcpversion); (void) setreuid(uid,uid); exit(1);
X     case 'W': (void) err(authtcpwarranty); (void) setreuid(uid,uid); exit(1);
X     case '?': (void) err(authtcpusage); (void) setreuid(uid,uid); exit(1);
X    }
X argv += optind, argc -= optind;
X while (*argv)
X  {
X   if (strinetaddr == NULL)
X     strinetaddr = *argv;
X   else if (strtcpport == NULL)
X     strtcpport = *argv;
X   else
X    {
X     program = argv;
X     break;
X    }
X   argv++;
X  }
X
X if ((program == NULL) || (*program == NULL))
X  {
X   err(authtcpusage); (void) setreuid(uid,uid); exit(1);
X   /* what else can you say? */
X  }
X 
X t = strlen(strinetaddr) - 1;
X if (isascii(strinetaddr[t]) && isdigit(strinetaddr[t]))
X   in = inet_addr(strinetaddr);
X else
X   if ((he = gethostbyname(strinetaddr)) == NULL)
X     in = (unsigned long) -1;
X   else
X     in = *((unsigned long *) (he->h_addr));
X
X if (in == (unsigned long) -1)
X  {
X   errn2("authtcp: fatal: do not understand inetaddr %s",strinetaddr);
X   (void) setreuid(uid,uid);
X   exit(1);
X  }
X
X t = strlen(strtcpport) - 1;
X if (isascii(strtcpport[t]) && isdigit(strtcpport[t]))
X   remoteport = atoi(strtcpport); /* so who cares if it's zero? */
X else
X   if ((se = getservbyname(strtcpport,"tcp")) == NULL)
X     remoteport = 0;
X   else
X     remoteport = ntohs(se->s_port); /* inconsistency alert! */
X				     /* se->s_port is int! */
X
X /* We will depend on the type of localport and remoteport, namely */
X /* unsigned short, to keep them between 0 and 65535. */
X
X /* The parent program must remain active and setuid auth, to wait for */
X /* the child to finish and to remove its authentication entry. But the */
X /* TCP connection must be made as the user---don't want fake rlogins! */
X
X /* We now switch to the real user ID. */
X
X if (setreuid(euid,uid))
X  {
X   perrn2("%s","authtcp: fatal: cannot setreuid");
X   (void) setreuid(uid,uid);
X   exit(1);
X  }
X
X if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) /* no security problem */
X  {
X   perrn2("%s","authtcp: fatal: cannot create socket");
X   (void) setreuid(uid,uid);
X   exit(1);
X  }
X
X /* The bind() below used to be if (localport) only. However, connect() */
X /* can take arbitrarily long, there's no solution like select-accept, */
X /* and authd should not have to wait to get its information. So we have */
X /* to find our local port now. This is documented to work in PS1:8-31. */
X
X sa.sin_family = AF_INET;
X sa.sin_port = htons(localport); /* ever seen a client do this before? */
X sa.sin_addr.s_addr = INADDR_ANY; /* or this? */
X 
X if (bind(s,&sa,sizeof(sa)) == -1)
X   if (localport)
X    {
X     perrn2("authtcp: fatal: cannot bind local port %u",
X            (unsigned int) localport);
X     (void) setreuid(uid,uid);
X     exit(1);
X    }
X   else
X    {
X     perrn2("%s","authtcp: fatal: cannot bind local port");
X     (void) setreuid(uid,uid);
X     exit(1);
X    }
X
X /* We now switch back to auth... */
X if (setreuid(uid,euid))
X  {
X   perrn2("%s","authtcp: fatal: cannot setreuid");
X   (void) setreuid(uid,uid);
X   exit(1);
X  }
X
X if (flagauth)
X  {
X   dummy = sizeof(sa);
X   if ((pw = getpwuid(uid)) == NULL)
X    {
X     errn("authtcp: fatal: cannot authenticate: who are you?");
X     (void) setreuid(uid,uid);
X     exit(1);
X    }
X   if (getsockname(s,&sa,&dummy) == -1)
X    {
X     perrn2("%s","authtcp: fatal: cannot get socket name");
X     (void) setreuid(uid,uid);
X     exit(1);
X    }
X   (void) sprintf(authfn,"%s/tcp/%D.%u.%u",AUTHDIR,in,
X		  (unsigned int) ntohs(sa.sin_port),(unsigned int) remoteport);
X   (void) sprintf(lockfn,"%s/tcp/lock.%u",AUTHDIR,
X		  (unsigned int) ntohs(sa.sin_port));
X   if (((lockfd = open(lockfn,O_WRONLY | O_CREAT | O_EXCL,0600)) == -1)
X     &&(((lockfd = open(lockfn,O_RDONLY)) == -1)
X	||(flock(lockfd,LOCK_EX) == -1)
X        ||(read(lockfd,lockbuf,31) <= 0)
X	||((lockbuf[0] != '!')
X          &&((atoi(lockbuf) <= 0)
X             ||(kill(atoi(lockbuf),0) == 0))) /* okay, screw the last process */
X	||(close(lockfd) == -1) /* impossible */
X        ||((lockfd = open(lockfn,O_WRONLY | O_CREAT | O_TRUNC,0600)) == -1)))
X    { /* yikes, that was incomprehensible */
X     errn2("authtcp: fatal: local port %u locked",
X	   (unsigned int) ntohs(sa.sin_port));
X     (void) setreuid(uid,uid);
X     exit(1);
X    }
X   (void) flock(lockfd,LOCK_EX);
X   (void) sprintf(lockbuf,"%d:%D.%u\n%s",getpid(),in,(unsigned int) remoteport,
X	          pw->pw_name);
X   (void) write(lockfd,lockbuf,strlen(lockbuf));
X   (void) flock(lockfd,LOCK_UN);
X  }
X /* It isn't a disaster if the user kills us now; it's just a possible */
X /* denial of service for later auths on this port. The amount of effort */
X /* necessary to guarantee that denial is huge. */
X
X /* We now switch back to real... */
X if (setreuid(euid,uid))
X  {
X   perrn2("%s","authtcp: fatal: cannot setreuid");
X   (void) setreuid(uid,euid);
X   if (flagauth)
X     (void) unlink(lockfn);
X   (void) setreuid(uid,uid);
X   exit(1);
X  }
X
X sa.sin_family = AF_INET;
X sa.sin_port = htons(remoteport);
X sa.sin_addr.s_addr = in;
X
X if (connect(s,&sa,sizeof(sa)) == -1)
X  {
X   perrn2("authtcp: fatal: cannot connect to %s",strinetaddr);
X   (void) setreuid(uid,euid);
X   if (flagauth)
X     (void) unlink(lockfn);
X   (void) setreuid(uid,uid);
X   exit(1);
X  }
X
X if (flagverbose)
X  {
X   inet.s_addr = in;
X   fprintf(stderr,"authtcp: connected to %s port %u\n",
X	   inet_ntoa(inet),(unsigned int) remoteport);
X  }
X
X /* We now have the uids switched, the connection open in socket s. */
X
X /* We're about to write the authentication entry. We must remove it */
X /* before coming back to the real uid and/or exiting. On the other */
X /* hand, we must fork as the real uid: one system stupidity is that */
X /* fork() uses the effective uid for MAXUPRC checks. Hence we fork */
X /* as the real (i.e. switched) uid, before authentication. In fact, */
X /* we delay authentication until it's convenient in the parent; the */
X /* child program could run before the authentication file exists. */
X /* This is not a race condition, for reasons explained in dir.doc. */
X
X if ((f = fork()) == 0) /* child */
X  {
X   if (setreuid(uid,uid))
X    {
X     perrn2("%s","authtcp: fatal: cannot setreuid");
X     exit(1);
X    }
X   if (filedesc)
X    {
X     if (dup2(s,filedesc) == -1)
X      {
X       perrn2("%s","authtcp: fatal: cannot use file descriptor");
X       exit(1);
X      }
X    }
X   else
X    {
X     (void) sprintf(strfd,"%d",s);
X     while (*(++argv))
X       if ((**argv == '=') && (*(*argv + 1) == '\0'))
X	{
X	 *argv = strfd;
X	 break;
X	}
X     /* don't need to complain about failure here */
X    }
X
X   if (flagremote)
X    {
X     unsigned long in; /* keep confirming variables separate */
X     unsigned short local;
X     unsigned short remote;
X     char *ruser;
X     char *srem;
X     char **t;
X     char **trem;
X     char **tproto;
X     char **envbak;
X
X     if (auth_fd(s,&in,&local,&remote) == -1)
X      {
X       perrn2("%s","authtcp: fatal: cannot confirm connection");
X       exit(1);
X      }
X     if ((ruser = auth_tcpuser(in,local,remote)) == NULL)
X       ruser = ""; /* bummer */
X     if ((srem = malloc(strlen(ruser) + 30)) == NULL)
X      {
X       perrn2("%s","authtcp: fatal: cannot allocate environment");
X       exit(1);
X      }
X     inet.s_addr = in;
X     sprintf(srem,"REMOTE=%s@%s",ruser,inet_ntoa(inet));
X     for (trem = envp;*trem;trem++)
X       if (strncmp(*trem,"REMOTE=",7) == 0)
X         break;
X     for (tproto = envp;*tproto;tproto++)
X       if (strncmp(*tproto,"PROTO=",6) == 0)
X         break;
X     if (!(*trem && *tproto))
X      {
X       envbak = envp;
X       if ((environ = (char **) malloc((trem - envp + 3) * sizeof(char *)))
X	   == NULL)
X        {
X         perrn2("%s","authtcp: fatal: cannot allocate environment");
X         exit(1);
X        }
X       for (t = envbak;*t;t++)
X         environ[t - envbak] = *t; /* not worth a bcopy */
X       trem = environ + ((*trem ? trem : t++) - envbak);
X       tproto = environ + ((*tproto ? tproto : t++) - envbak);
X       environ[t - envbak] = NULL;
X      }
X     *trem = srem;
X     *tproto = "PROTO=TCP";
X     /* XXXXXX: Should we do confirming sanity checks here? */
X    }
X
X   (void) signal(SIGINT,flagsigintign ? SIG_IGN : SIG_DFL);
X   (void) signal(SIGQUIT,flagsigquitign ? SIG_IGN : SIG_DFL);
X   (void) signal(SIGTSTP,flagsigtstpign ? SIG_IGN : SIG_DFL);
X   (void) signal(SIGHUP,flagsighupign ? SIG_IGN : SIG_DFL);
X   (void) signal(SIGALRM,flagsigalrmign ? SIG_IGN : SIG_DFL);
X   (void) signal(SIGXCPU,flagsigxcpuign ? SIG_IGN : SIG_DFL);
X   (void) signal(SIGXFSZ,flagsigxfszign ? SIG_IGN : SIG_DFL);
X   (void) signal(SIGVTALRM,flagsigvtalrmign ? SIG_IGN : SIG_DFL);
X   (void) signal(SIGPROF,flagsigprofign ? SIG_IGN : SIG_DFL);
X
X   /* Yes, Virginia, this is portable. Read execvp(3). */
X   /* Annoying that there isn't a better interface, though. */
X   (void) execvp(*program,program); /* must use environ! */
X   perrn2("authtcp: fatal: cannot execute %s",*program);
X   exit(1);
X  }
X /* no need to test for failing fork */
X
X if (setreuid(uid,euid))
X  {
X   perrn2("%s","authtcp: fatal: cannot setreuid");
X   (void) setreuid(uid,euid);
X   if (flagauth)
X     (void) unlink(lockfn);
X   (void) setreuid(uid,uid);
X   exit(1);
X  }
X /* Now we're back to setuid auth in the parent, as we will remain. */
X
X if (flagauth)
X  {
X   /* authfn and pw are put together up above */
X   if ((authfd = open(authfn,O_WRONLY | O_CREAT | O_EXCL,0600)) == -1)
X    {
X     perrn2("%s","authtcp: fatal: cannot authenticate");
X     (void) setreuid(uid,euid);
X     if (flagauth)
X       (void) unlink(lockfn);
X     (void) setreuid(uid,uid);
X     exit(1);
X    }
X   (void) write(authfd,pw->pw_name,strlen(pw->pw_name));
X   (void) close(authfd); /* if it fails, tough luck. */
X  }
X
X (void) signal(SIGCONT,sigcont);
X
X /* Unless the user is auth we won't receive any signals. So the */
X /* following handlers are mainly a convenience on machines where */
X /* authtcp is not installed by the sysadmin. */
X (void) signal(SIGTERM,SIG_IGN); /* kill child, not us! */
X (void) signal(SIGTTOU,SIG_DFL); /* for stopping */
X (void) signal(SIGTTIN,SIG_DFL); /* for stopping */
X
X /* We're going to leave the socket open, to completely close a possible */
X /* security hole. The application must not depend upon the connection */
X /* disappearing when it's closed. */
X
X for (t = getdtablesize();t >= 0;t--)
X   if ((s != t) && (t != 2)) /* have to leave stderr open! */
X     (void) close(t); /* might as well do a bit of dissociation */
X
X while (wait3(&status,WUNTRACED,(struct rusage *) NULL) >= 0)
X  {
X   if (status.w_stopval == WSTOPPED) /* if child stops, we stop */
X    {
X     flagcont = 0;
X     (void) signal(SIGTSTP,SIG_DFL); /* for stopping */
X     switch(status.w_stopsig)
X      { /* this just looks good */
X       case SIGSTOP: (void) kill(getpid(),SIGSTOP); break;
X       case SIGTSTP: (void) kill(getpid(),SIGTSTP); break;
X       case SIGTTOU: (void) kill(getpid(),SIGTTOU); break;
X       case SIGTTIN: (void) kill(getpid(),SIGTTIN); break;
X       default: (void) kill(getpid(),SIGSTOP); break;
X      }
X     while (flagcont == 0)
X       ; /* rack up CPU time waiting for the CONT---is there a better way? */
X     (void) signal(SIGTSTP,SIG_IGN);
X     (void) kill(f,SIGCONT); /* when we start, child starts */
X     continue;
X    }
X   if (flagverbose)
X     if (WIFSIGNALED(status))
X       if (status.w_coredump)
X	{
X	 errn2("authtcp: fatal: killed by signal %d (core dumped)",
X               status.w_termsig);
X	}
X       else
X	{
X	 errn2("authtcp: fatal: killed by signal %d",status.w_termsig);
X	}
X   break;
X  }
X
X if (flagauth)
X  {
X   if (unlink(authfn) == -1) /* had better succeed! */
X    {
X     perrn2("authtcp: fatal: cannot unlink authentication entry %s",authfn);
X     exit(1);
X    }
X   (void) unlink(lockfn);
X  }
X
X exit(WIFEXITED(status) ? ((int) status.w_retcode) : 1);
X}
END_OF_FILE
if test 19108 -ne `wc -c <'authtcp.c'`; then
    echo shar: \"'authtcp.c'\" unpacked with wrong size!
fi
# end of 'authtcp.c'
fi
if test -f 'rfc931' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rfc931'\"
else
echo shar: Extracting \"'rfc931'\" \(9196 characters\)
sed "s/^X//" >'rfc931' <<'END_OF_FILE'
X
X
X
X
X
X---------
X
X
X< INC-PROJECT, AUTH-RFC-VER-2.NLS.5, >, 7-Jan-85 17:18-PST JBP
X;;;;
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XStJohns                                                         [Page 0]
X
X
XNetwork Working Group                                       Mike StJohns
XRequest for Comments: 931                                           TPSC
XSupersedes: RFC 912                                         January 1985
X
X                         Authentication Server
X
X
XSTATUS OF THIS MEMO
X
X   This RFC suggests a proposed protocol for the ARPA-Internet
X   community, and requests discussion and suggestions for improvements.
X   This is the second draft of this proposal (superseding RFC 912) and
X   incorporates a more formal description of the syntax for the request
X   and response dialog, as well as a change to specify the type of user
X   identification returned.  Distribution of this memo is unlimited.
X
XINTRODUCTION
X
X   The Authentication Server Protocol provides a means to determine the
X   identity of a user of a particular TCP connection.  Given a TCP port
X   number pair, it returns a character string which identifies the owner
X   of that connection on the server's system.  Suggested uses include
X   automatic identification and verification of a user during an FTP
X   session, additional verification of a TAC dial up user, and access
X   verification for a generalized network file server.
X
XOVERVIEW
X
X   This is a connection based application on TCP.  A server listens for
X   TCP connections on TCP port 113 (decimal).  Once a connection is
X   established, the server reads one line of data which specifies the
X   connection of interest.  If it exists, the system dependent user
X   identifier of the connection of interest is sent out the connection.
X   The service closes the connection after sending the user identifier.
X
XRESTRICTIONS
X
X   Queries are permitted only for fully specified connections. The
X   local/foreign host pair used to fully specify the connection are
X   taken from the query connection.  This means a user on Host A may
X   only query the server on Host B about connections between A and B.
X
X
X
X
X
X
X
X
X
X
X
X
XStJohns                                                         [Page 1]
X
X
XRFC 931                                                     January 1985
XAuthentication Server
X
X
XQUERY/RESPONSE FORMAT
X
X   The server accepts simple text query requests of the form
X
X      <local-port>, <foreign-port>
X
X   where <local-port> is the TCP port (decimal) on the target (server)
X   system, and <foreign-port> is the TCP port (decimal) on the source
X   (user) system.
X
X      For example:
X
X         23, 6191
X
X   The response is of the form
X
X      <local-port>, <foreign-port> : <response-type> : <additional-info>
X
X   where <local-port>,<foreign-port> are the same pair as the query,
X   <response-type> is a keyword identifying the type of response, and
X   <additional info> is context dependent.
X
X      For example:
X
X         23, 6191 : USERID : MULTICS : StJohns.DODCSC.a
X         23, 6193 : USERID : TAC : MCSJ-MITMUL
X         23, 6195 : ERROR : NO-USER
X
XRESPONSE TYPES
X
X   A response can be one of two types:
X
X   USERID
X
X      In this case, <additional-info> is a string consisting of an
X      operating system name, followed by a ":", followed by user
X      identification string in a format peculiar to the operating system
X      indicated.  Permitted operating system names are specified in
X      RFC-923, "Assigned Numbers" or its successors.  The only other
X      names permitted are "TAC" to specify a BBN Terminal Access
X      Controller, and "OTHER" to specify any other operating system not
X      yet registered with the NIC.
X
X
X
X
X
X
X
XStJohns                                                         [Page 2]
X
X
XRFC 931                                                     January 1985
XAuthentication Server
X
X
X   ERROR
X
X      For some reason the owner of <TCP-port> could not be determined,
X      <additional-info> tells why.  The following are suggested values
X      of <additional-info> and their meanings.
X
X      INVALID-PORT
X
X         Either the local or foreign port was improperly specified.
X
X      NO-USER
X
X         The connection specified by the port pair is not currently in
X         use.
X
X      UNKNOWN-ERROR
X
X         Can't determine connection owner; reason unknown.  Other values
X         may be specified as necessary.
X
XCAVEATS
X
X   Unfortunately, the trustworthiness of the various host systems that
X   might implement an authentication server will vary quite a bit.  It
X   is up to the various applications that will use the server to
X   determine the amount of trust they will place in the returned
X   information.  It may be appropriate in some cases restrict the use of
X   the server to within a locally controlled subnet.
X
XAPPLICATIONS
X
X   1) Automatic user authentication for FTP
X
X      A user-FTP may send a USER command with no argument to the
X      server-FTP to request automatic authentication.  The server-FTP
X      will reply with a 230 (user logged in) if it can use the
X      authentication.  It will reply with a 530 (not logged in) if it
X      cannot authenticate the user.  It will reply with a 500 or 501
X      (syntax or parameter problem) if it does not implement automatic
X      authentication.  Please note that no change is needed to currently
X      implemented servers to handle the request for authentication; they
X      will reject it normally as a parameter problem.  This is a
X      suggested implementation for experimental use only.
X
X   2) Verification for privileged network operations.  For example,
X   having the server start or stop special purpose servers.
X
X
X
XStJohns                                                         [Page 3]
X
X
XRFC 931                                                     January 1985
XAuthentication Server
X
X
X   3) Elimination of "double login" for TAC and other TELNET users.
X
X      This will be implemented as a TELNET option.
X
XFORMAL SYNTAX
X
X   <request>     ::= <port-pair> <CR> <LF>
X
X   <port-pair>   ::= <integer-number> "," <integer-number>
X
X   <reply>       ::= <reply-text> <CR> <LF>
X
X   <reply-text>  ::= <error-reply> | <auth-reply>
X
X   <error-reply> ::= <port-pair> ":" ERROR ":" <error-type>
X
X   <auth-reply>  ::= <port-pair> ":" USERID ":" <opsys> ":" <user-id>
X
X   <error-type>  ::= INVALID-PORT | NO-USER | UNKNOWN-ERROR
X
X   <opsys>       ::= TAC | OTHER | MULTICS | UNIX ...etc.
X                     (See "Assigned Numbers")
X
X   Notes on Syntax:
X
X      1)  White space (blanks and tab characters) between tokens is not
X      important and may be ignored.
X
X      2)  White space, the token separator character (":"), and the port
X      pair separator character (",") must be quoted if used within a
X      token.  The quote character is a back-slash, ASCII 92 (decimal)
X      ("\").  For example, a quoted colon is "\:".  The back-slash must
X      also be quoted if its needed to represent itself ("\\").
X
XNotes on User Identification Format:
X
X   The user identifier returned by the server should be the standard one
X   for the system.  For example, the standard Multics identifier
X   consists of a PERSONID followed by a ".", followed by a PROJECTID,
X   followed by a ".", followed by an INSTANCE TAG of one character.  An
X   instance tag of "a" identifies an interactive user, and instance tag
X   of "m" identifies an absentee job (batch job) user, and an instance
X   tag of "z" identifies a daemon (background) user.
X
X   Each set of operating system users must come to a consensus as to
X
X
X
X
XStJohns                                                         [Page 4]
X
X
XRFC 931                                                     January 1985
XAuthentication Server
X
X
X   what the OFFICIAL user identification for their systems will be.
X   Until they register this information, they must use the "OTHER" tag
X   to specify their user identification.
X
XNotes on User Identification Translation:
X
X   Once you have a user identifier from a remote system, you must then
X   have a way of translating it into an identifier that meaningful on
X   the local system.  The following is a sketchy outline of table driven
X   scheme for doing this.
X
X   The table consists of four columns, the first three are used to match
X   against, the fourth is the result.
X
X      USERID              Opsys     Address     Result
X      MCSJ-MITMUL         TAC       26.*.*.*    StJohns
X      *                   MULTICS   192.5.42.*  =
X      *                   OTHER     10.0.0.42   anonymous
X      MSJ                 ITS       10.3.0.44   StJohns
X
X   The above table is a sample one for a Multics system on MILNET at the
X   Pentagon.  When an authentication is returned, the particular
X   application using the userid simply looks for the first match in the
X   table.  Notice the second line.  It says that any authentication
X   coming from a Multics system on Net 192.5.42 is accepted in the same
X   format.
X
X   Obviously, various users will have to be registered to use this
X   facility, but the registration can be done at the same time the use
X   receives his login identity from the system.
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XStJohns                                                         [Page 5]
END_OF_FILE
if test 9196 -ne `wc -c <'rfc931'`; then
    echo shar: \"'rfc931'\" unpacked with wrong size!
fi
# end of 'rfc931'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
exit 0 # Just in case...
