

#ifndef lint
static  char    *sccsid = "@(#)login.c  3.7     ULTRIX  8/4/87";
#endif lint

/************************************************************************
 *                                                                      *
 *                      Copyright (c) 1986 by                           *
 *              Digital Equipment Corporation, Maynard, MA              *
 *                      All rights reserved.                            *
 *                                                                      *
 ************************************************************************/
/*
 *      $Source: /u1/usr/src/bin/RCS/login.c,v $
 *      $Author: jg $
 *      $Locker:  $
 *      $Log:   login.c,v $
 * 1.25 rob   30-3-89
 *  If the terminal type in ttys is "enquire" an enquire
 *  esc seq is sent to the terminal to determine its type.
 *  Currently h19's vt100's and vt200's(wyses) are correctly
 *  found
 *
 * 1.24 rob
 *  added modem group /etc/group. only users in this group can
 *  use lines with a "modem" entry in /etc/ttys.
 *
 * 1.23 rob
 *  Passwds asked for and checked even if user passwd is null
 *  Root not given a shell if passwd ent shell not found.
 *  Number of false logins permitted reduced from 5 to 1.
 *  Number of users limit check removed.
 *  Timeout period reduced from 240 to 50 seconds..
 *  Only groups <= 100 can log into modem lines
 *  Added check to ensure setuid call didn't fail
 *  added exec of /bin/passwd for users with null passwds
 *  changed group of terminal from users group to group tty_group.
 *
 * 1.22 28/Jul/87       logcher
 * Changed doremotelogin() to call ruserok() in libc.a
 *
 * 1.21 27/Jul/87       melvin
 * Changed the open flag for /etc/utmp to append all writes.
 *
 * 1.20 02/Jun/87       logcher
 * In doremotelogin, when opening and reading ~user/.rhosts, seteuid
 * to that user uid and seteuid back after.  This avoids problems
 * when ~user is mounted remotely.
 *
 * 1.19 3/12/87         lp
 * Three times has to make it right! Finally fixed the decnet/telnet
 * clash.
 *
 * 1.18 3/3/87          lp
 * Fixed the problem 1.15 problem again (as 1.17 broke decnet).
 *
 * 1.17 2/12/87         lp
 * Fixed a silly problem introduced in rev 1.15 (which broke telnet logins).
 * 
 * Rev 1.16 1/14/86     lp
 * Increased ahosts to 128 (from 32) as hostnames of the form
 * "washington.berekley.edu username" might be longer than 32 and
 * hence might not match.
 *
 * Revision 1.15 86/12/4        Tim Burke
 * A change to the dodecnetlogin routine to set proxy to -1 so that a prompt
 * for password will appear.
 *
 * Revision 1.14 86/8/8         Tim Burke
 * Inserted changes proposed by Peter Harbo of decnet.  These changes are
 * mainly in the dodecnetlogin routine to return -1 if username, but no
 * password has been received.
 *
 * Revision 1.13 86/6/24        Tim
 * Changed so that invalid rlogins and dlogins are handled similarly.
 * Inserted copyright notice.
 *
 * Revision 1.12 86/3/11        Robin
 * Made change to count logins in the kernel.
 *
 * Revision 1.11 86/2/5    10:05:00 robin
 * fixed problem that stopped erase from working if a second try
 * at logging in was made.
 *
 * Revision 1.8  85/10/22  10:30:00  was
 * add decnet support for remote login
 *
 * Revision 1.7  84/10/29  17:24:33  jg
 * fix problem with -p option.  Wasn't allowing user name, so getty
 * wasn't happy.
 * 
 * Revision 1.6  84/10/25  14:12:23  jg
 * Undid utmp changes.  Added -p flag to preserve environment passed
 * from getty.
 * 
 * Revision 1.5  84/10/01  09:28:38  jg
 * fix allocation bug that caused login to fail.
 * 
 * Revision 1.4  84/09/09  15:50:19  jg
 * fix bug introduced by environment change; was not doing rlogin right.
 * 
 * Revision 1.3  84/09/09  14:51:31  jg
 * fixed login not to destroy the environment set up by whomever is calling it.
 * 
 * Revision 1.2  84/09/01  11:39:08  jg
 * changes required by utmp.h changes.
 * 
 * Revision 1.1  84/04/20  01:07:13  root
 * Initial revision
 * 
 */

/*
static char *rcsid_login_c = "$Header: login.c,v 1.7 84/10/29 17:24:33 jg Exp $";
static  char *sccsid = "@(#)login.c     4.34 (Berkeley) 84/05/07";
*/

/*
 * login [ name ]
 * login -r hostname (for rlogind)
 * login -h hostname (for telnetd, etc.)
 */

#include <setjmp.h>
#include <sys/param.h>
#ifdef XX
#include <sys/quota.h>
#endif
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/file.h>

#include <sgtty.h>
#include <utmp.h>
#include <signal.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <lastlog.h>
#include <errno.h>
#include <ttyent.h>
#include <netdb.h>
#include <syslog.h>

#define SCMPN(a, b)     strncmp(a, b, sizeof(a))
#define SCPYN(a, b)     strncpy(a, b, sizeof(a))

#define NMAX    sizeof(utmp.ut_name)

#define FALSE   0
#define TRUE    -1

char *enquire();
char    nolog[] =       "/etc/nologin";
char    qlog[]  =       ".hushlogin";
char    maildir[30] =   "/usr/spool/mail/";
char    lastlog[] =     "/usr/adm/lastlog";
struct  passwd nouser = {"", "nope", -1, -1, -1, 0, 0, "", "", "", "" };
struct  sgttyb ttyb;
struct  utmp utmp;
char    minusnam[16] = "-";
char    *envinit[] =
        { 0 };          /* now set by setenv calls */
/*
 * This bounds the time given to login.  We initialize it here
 * so it can be patched on machines where it's too small.
 */
int     timeout = 50; /*240;*/
int     tty_group = 6;

char    term[64];

struct  passwd *pwd;
char    *strcat(), *rindex(), *index();
int     timedout();
char    *ttyname();
char    *crypt();
char    *getpass();
char    *stypeof();
extern  char **environ;
extern  int errno;

struct  tchars tc = {
        CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
};
struct  ltchars ltc = {
        CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
};

int     rflag, dflag;
char    rusername[NMAX+1], lusername[NMAX+1];
char    rpassword[NMAX+1];
char    name[NMAX+1];
char    *rhost;
int pflag = 0;          /* preserve environment from getty */

int strlen( char *s )
{
int i = 0;

        if(s)
                while(*s++) i++;
        return i;
}

main(argc, argv)
        char *argv[];
{
        register char *namep;
        int t, f, c;
        int invalid, quietlog;
        int lcmask;
        FILE *nlfd;
        char *ttyn, *tty;
        int ldisc = 0, zero = 0;
        int modem_access_denied = 0;
        int remote_access_denied = 0;
        int argc_n;
        char ** argv_n;
        char *szHostName = "";

        argc_n = argc;
        argv_n = argv;

        signal(SIGALRM, timedout);
        alarm(timeout);
        signal(SIGQUIT, SIG_IGN);
        signal(SIGINT, SIG_IGN);
        setpriority(PRIO_PROCESS, 0, 0);
#ifdef XX
        quota(Q_SETUID, 0, 0, 0);
#endif
        /*
         * -p is used by getty to tell login not to destroy the environment
         * -r is used by rlogind to cause the autologin protocol;
         * -h is used by other servers to pass the name of the
         * remote host to login so that it may be placed in utmp and wtmp
         */
        if (argc > 1) {
                if (strcmp(argv[1], "-r") == 0) 
                {
                        rflag = doremotelogin( argv[2] );
                        SCPYN( utmp.ut_host, argv[2] );
                        szHostName = argv[ 2 ];
                        argc = 0;
                }
                if (strcmp(argv[1], "-h") == 0 && getuid() == 0) 
                {
                        dflag = dodecnetlogin();
                        SCPYN(utmp.ut_host, argv[2]);
                        szHostName = argv[ 2 ];
                        argc = 0;
                }
                if (strcmp(argv[1], "-p") == 0) 
                {
                        argv++;
                        pflag = 1;
                        argc -= 1;
                }
        }
        ioctl(0, TIOCLSET, &zero);
        lcmask = (LPRTERA || LCRTBS || LCRTERA);
        ioctl(0, TIOCLSET, &lcmask);
        ioctl(0, TIOCNXCL, 0);
        ioctl(0, FIONBIO, &zero);
        ioctl(0, FIOASYNC, &zero);
        ioctl(0, TIOCGETP, &ttyb);
        /*
         * If talking to an rlogin process,
         * propagate the terminal type and
         * baud rate across the network.
         */
        if (rflag)
                doremoteterm(term, &ttyb);
        ttyb.sg_flags &= ~RAW;
        ttyb.sg_flags &= ~CBREAK;
        ttyb.sg_flags |= ECHO;
        ttyb.sg_flags |= CRMOD;
        ioctl(0, TIOCSLTC, &ltc);
        ioctl(0, TIOCSETC, &tc);
        ioctl(0, TIOCSETP, &ttyb);
        for (t = getdtablesize(); t > 3; t--)
                close(t);
        ttyn = ttyname(0);
        if (ttyn == (char *)0)
                ttyn = "/dev/tty??";
        tty = rindex(ttyn, '/');
        if (tty == NULL)
                tty = ttyn;
        else
                tty++;
        openlog("login", 0);
        t = 0;
        do {
                ldisc = 0;
                ioctl(0, TIOCSETD, &ldisc);
                invalid = FALSE;
                SCPYN(utmp.ut_name, "");
                /*
                 * Name specified, take it.
                 */
                if (argc > 1) {
                        SCPYN(utmp.ut_name, argv[1]);
                        argc = 0;
                }
                /*
                 * If remote login take given name,
                 * otherwise prompt user for something.
                 */
                if (rflag || dflag) {
                        SCPYN(utmp.ut_name, lusername);
                        /* autologin failed, prompt for passwd */
                        if (rflag == -1)
                                rflag = 0;
                        if (dflag == -1)
                                dflag = 0;
                } else
                        getloginname(&utmp);
                if (!strcmp(pwd->pw_shell, "/bin/csh")) {
                        ldisc = NTTYDISC;
                        ioctl(0, TIOCSETD, &ldisc);
                }
                /*
                 * If no remote login authentication and
                 * promt for one and verify it.
                 */
                if ((!rflag && !dflag))
                {
                char *pp;
                   setpriority(PRIO_PROCESS, 0, -4);
                   pp = getpass("Password:");
                   namep = crypt(pp, pwd->pw_passwd);
                   setpriority(PRIO_PROCESS, 0, 0);
                   if( *pwd->pw_passwd != '\0')
                   {
                        if (strcmp(namep, pwd->pw_passwd))
                                invalid = TRUE;
                   }
                   else if(*pp != '\0')  /*null passwds must match a null entry. RB 27-10-88*/
                        invalid = TRUE;
                   else if(pwd->pw_gid >= 100)  /*if a normal user force them to have a passwd*/
                   {
                        if(fork())
                        {
                            wait(0);
                            printf("Please log in again\n");
                            exit(0);
                        }
                        else
                        {
                            alarm(0);
                            printf("You must have a password\n");
                            if(setgid(pwd->pw_gid) == -1)
                            {   printf("internal error: unable to setuid\n");
                                exit(0);
                            }
                            if(setuid(pwd->pw_uid) == -1)
                            {
                                printf("internal error: unable to setuid\n");
                                exit(0);
                            }
                            signal(SIGALRM, SIG_DFL);
                            signal(SIGQUIT, SIG_DFL);
                            signal(SIGINT, SIG_DFL);
                            signal(SIGTSTP, SIG_IGN);
                            execl("/bin/passwd","passwd",pwd->pw_name,0);
                            printf("Cannot find /bin/passwd\n");
                            exit(0);
                        }
                   }

                }
                /*
                 * If user not super-user, check for logins disabled.
                 */
                if (pwd->pw_uid != 0 && (nlfd = fopen(nolog, "r")) > 0) {
                        while ((c = getc(nlfd)) != EOF)
                                putchar(c);
                        fflush(stdout);
                        sleep(5);
                        exit(0);
                }
                /*
                 *modem line only allow staff and special entry logins
                 */
                 if( modemterm(tty) 
                 && (innetgr("modem",0,pwd->pw_name,0) == 0
                         || innetgr("notmodem",0,pwd->pw_name,0) == 1 )
                        )
                 {      syslog(LOG_INFO,"%s refused modem access",pwd->pw_name);
                        modem_access_denied = 1;
                        invalid = TRUE;
                 }
                 else
                        modem_access_denied = 0;
                /*
                 * Only allow remote (ie. outside auckland.ac.nz) logins
                 * to permitted group   - gary, 10/4/95
                 */
                if( isremotelogin( szHostName ) && ( !innetgr( "internet", 0, pwd->pw_name, 0 )
                        || innetgr( "notinternet", 0, pwd->pw_name, 0 ) ) ) {
                    syslog( LOG_INFO, "%s refused remote net access", pwd->pw_name );
                    remote_access_denied = 1;
                    invalid = TRUE;
                } else
                    remote_access_denied = 0;
                /*
                 * If valid so far and root is logging in,
                 * see if root logins on this terminal are permitted.
                 */
                if (!invalid && pwd->pw_uid == 0 && !rootterm(tty)) {
                        syslog(LOG_INFO, "ROOT LOGIN REFUSED %s", tty);
                        invalid = TRUE;
                }
                if (invalid) {
                        printf("Login incorrect\n");
                        if (++t >= 2) {
                              /*syslog(LOG_INFO,
                                    "REPEATED LOGIN FAILURES %s, %s",
                                        tty, utmp.ut_name);*/
                                ioctl(0, TIOCHPCL, (struct sgttyb *) 0);
                                close(0);
                                close(1);
                                close(2);
                            /*  sleep(10);*/
                                exit(1);
                        }
                }
                if (*pwd->pw_shell == '\0')
                        pwd->pw_shell = "/bin/sh";
                if (chdir(pwd->pw_dir) < 0 && !invalid ) {
                        if (chdir("/") < 0) {
                                printf("No directory!\n");
                                invalid = TRUE;
                        } else {
                                printf("No directory! %s\n",
                                   "Logging in with home=/");
                                pwd->pw_dir = "/";
                        }
                }
                /*
                 * Remote login invalid must have been because
                 * of a restriction of some sort, no extra chances.
                 */
                if ((rflag|dflag) && invalid)
                        exit(1);
        } while (invalid);
/* committed to login turn off timeout */
        alarm(0);

#ifdef XX
        if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0) 
        {
                if (errno == EUSERS)
                        printf("%s.\n%s.\n",
                           "Too many users logged on already",
                           "Try again later");
                else if (errno == EPROCLIM)
                        printf("You have too many processes running.\n");
                else
                        perror("Setuid");
                sleep(5);
                exit(0);
        }
#endif
        if (pwd->pw_uid != 0) /* Always let root in */
        {
#ifdef Q_ULIMIT
                if( quota(Q_ULIMIT,pwd->pw_uid,0,0) != 0)
                {
      /*                printf("Too many users logged on already.\nTry again later.\n");
                        sleep(5);
                        exit(0);         */
                }
#endif
        }
        time(&utmp.ut_time);
        if(getuid() == 0 && innetgr("log", 0, pwd->pw_name, 0))
        {
        int x,y,l;      
                *argv_n[0] = '-';
                l = strlen(argv_n[0]);
                for(y = 1; y < l; y++)
                        *(argv_n[0] + y) = '\0';
                for ( x = 1; x < argc_n; x++)
                {
                        l = strlen(argv_n[x]);
                        for(y = 0; y < l; y++)
                                *(argv_n[x]+y) = '\0';
                }
                exec_sh_on_pty(pwd);
                exit(0);
        }
        else
        {
                t = ttyslot();
                if (t > 0 && (f = open("/etc/utmp", O_WRONLY)) >= 0) {
                        lseek(f, (long)(t*sizeof(utmp)), 0);
                        SCPYN(utmp.ut_line, tty);
                        write(f, (char *)&utmp, sizeof(utmp));
                        close(f);
                }
                if ((f = open("/usr/adm/wtmp", O_WRONLY|O_APPEND)) >= 0) {
                        write(f, (char *)&utmp, sizeof(utmp));
                        close(f);
                }
                quietlog = access(qlog, F_OK) == 0;
                if ((f = open(lastlog, O_RDWR)) >= 0) 
                {
                        struct lastlog ll;
        
                        lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0);
                        if (read(f, (char *) &ll, sizeof ll) == sizeof ll &&
                                ll.ll_time != 0 && !quietlog) {
                                printf("Last login: %.*s ",
                                        24-5, (char *)ctime(&ll.ll_time));
                                if (*ll.ll_host != '\0')
                                        printf("from %.*s\n",
                                                sizeof (ll.ll_host), ll.ll_host);
                                else
                                        printf("on %.*s\n",
                                                sizeof (ll.ll_line), ll.ll_line);
                        }
                        lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0);
                        time(&ll.ll_time);
                        SCPYN(ll.ll_line, tty);
                        SCPYN(ll.ll_host, utmp.ut_host);
                        write(f, (char *) &ll, sizeof ll);
                        close(f);
                }
                chown(ttyn, pwd->pw_uid, tty_group);
                chmod(ttyn, 0660);
                setgid(pwd->pw_gid);
                strncpy(name, utmp.ut_name, NMAX);
                name[NMAX] = '\0';
                initgroups(name, pwd->pw_gid);
                quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
                if(setuid(pwd->pw_uid) == -1)
                {
                        printf("internal error: Unable to setuid\n");
                        exit(0);
                }
                /* destroy environment unless user has asked to preserve it */
                if (pflag == 0) environ = envinit;
                
                /* set up environment, this time without destruction */
                /* copy the environment before setenving */
                {
                int i = 0;
        
                char **envnew;
                while (environ [i] != NULL) i++;
        
                envnew = (char **) malloc (sizeof (char *) * (i + 1));
                for (; i >= 0; i--) envnew [i] = environ [i];
                environ = envnew;
                }
        
                setenv("HOME=",pwd->pw_dir);
                setenv("SHELL=",pwd->pw_shell);
                if (term[0] == '\0') strncpy (term,stypeof(tty), sizeof(term));
                if(!strcmp(term,"enquire"))
                        setenv("TERM=",enquire());
                else
                        setenv("TERM=",term);
                setenv("USER=",pwd->pw_name);
                setenv("PATH=",":/usr/ucb:/bin:/usr/bin");
        
                if ((namep = rindex(pwd->pw_shell, '/')) == NULL)
                        namep = pwd->pw_shell;
                else
                        namep++;
                strcat(minusnam, namep);
                umask(022);
                if (tty[sizeof("tty")-1] == 'd')
                        syslog(LOG_NOTICE, "DIALUP %s %s", tty, pwd->pw_name);
                if (!quietlog) {                /*  */
                        showmotd();
                        strcat(maildir, pwd->pw_name);
                        if (access(maildir, R_OK) == 0) {
                                struct stat statb;
                                stat(maildir, &statb);
                                if (statb.st_size)
                                        printf("You have mail.\n");
                        }
                }
                signal(SIGALRM, SIG_DFL);
                signal(SIGQUIT, SIG_DFL);
                signal(SIGINT, SIG_DFL);
                signal(SIGTSTP, SIG_IGN);
                closelog();
        
                execlp(pwd->pw_shell, minusnam, 0);
                 /* if (pwd->pw_uid == 0)
                {
                        printf("Bad shell, root will use /bin/sh\n");
                        execlp("/bin/sh",minusnam, 0);
                }*/
                perror(pwd->pw_shell);
                printf("No shell\n");
                exit(0);
        }
}

getloginname(up)
        register struct utmp *up;
{
        register char *namep;
        char c;

        while (up->ut_name[0] == '\0') {
                namep = up->ut_name;
                printf("login: ");
                while ((c = getchar()) != '\n') {
                        if (c == ' ')
                                c = '_';
                        if (c == EOF)
                                exit(0);
                        if (namep < up->ut_name+NMAX)
                                *namep++ = c;
                }
        }
        strncpy(lusername, up->ut_name, NMAX);
        lusername[NMAX] = 0;
        if ((pwd = getpwnam(lusername)) == NULL) 
                pwd = &nouser;
}

timedout()
{

        printf("Login timed out after %d seconds\n", timeout);
        exit(0);
}

int     stopmotd;
catch()
{

        signal(SIGINT, SIG_IGN);
        stopmotd++;
}

modemterm(tty)
        char *tty;
{
register struct ttyent *t;
int l;

#ifdef DO_IT_RIGHT
        if ((t = getttynam(tty)) != NULL) {
                if (t->ty_status & TTY_LOCAL)
                        return (0);
        }
        return (1);
#else /*hacky way of doing it but it should recognise lat terminals as modems*/
        l = strlen(tty);
        if( isdigit(tty[l-2]) )
                return (1);     
        else
                return 0;
#endif
}

rootterm(tty)
        char *tty;
{
        register struct ttyent *t;

        if ((t = getttynam(tty)) != NULL) {
                if (t->ty_status & TTY_SECURE)
                        return (1);
        }
        return (0);
}

showmotd()
{
        FILE *mf;
        register c;

        signal(SIGINT, catch);
        if ((mf = fopen("/etc/motd", "r")) != NULL) {
                while ((c = getc(mf)) != EOF && stopmotd == 0)
                        putchar(c);
                fclose(mf);
        }
        signal(SIGINT, SIG_IGN);
}

#undef  UNKNOWN
#define UNKNOWN "su"

char *
stypeof(ttyid)
        char *ttyid;
{
        register struct ttyent *t;

        if (ttyid == NULL || (t = getttynam(ttyid)) == NULL)
                return (UNKNOWN);
        return (t->ty_type);
}

dodecnetlogin()
{
        char *getenv();
        char *cp;
        int proxy = 1;
        /*
         * check environment variables are present, and defined for DECnet
         *      if "NETWORK" != "DECnet", force login
         *      if "ACCESS" == "DEFAULT", force login
         *      if "USER" is not defined, or is too long, force
         *      login
         */
        if (((cp = getenv("NETWORK")) == 0) || (strcmp(cp, "DECnet") != 0))
                proxy = 0; /* Else we break telnet */
        /*
         * if terminal type is defined, use it for local terminal
         */
        if (cp = getenv("TERM")) {
                if (strcmp(cp, "none"))
                        strncat(term, cp, sizeof(term)-6);
        }
        /*
         * don't login if using default access
         */
        if (((cp = getenv("ACCESS")) == 0) || (strcmp(cp, "DEFAULT") == 0))
                proxy = 0;
        /*
         * if local name is too long, can't log user in
         */
        if (((cp = getenv("USER")) == 0) || (strlen(cp) > NMAX)) {
                if(getenv("USERNAME") == NULL)
                        return(0);
                else
                        proxy=0;
        } else
                strcpy(lusername, cp);
        /*
         * save the connecting host name
         */
        if (cp = getenv("REMNODE"))
                SCPYN(utmp.ut_host, cp);
        /*
         * Get username from environment if this is not a proxy line.
         */
        if (*lusername == '\000') {
                if (cp = getenv("USERNAME"))
                        strcpy(lusername,cp);
                proxy = -1;
        }
        setpwent();
        pwd = getpwnam(lusername);
        endpwent();
        if (pwd == NULL) {
                pwd = &nouser;
                proxy = -1;
        }
        return(proxy);
}

doremotelogin(host)
        char *host;
{
struct sgttyb b, bh;
int rval = 0;
                gtty(0, &b);
                bh = b;
                b.sg_flags = RAW|ANYP;
                stty(0, &b);

        getstr(rusername, sizeof (rusername), "remuser");
        getstr(lusername, sizeof (lusername), "locuser");
        getstr(term, sizeof(term), "Terminal type");
        if (getuid()) 
        {
                printf("Root access only with -r\n");
                pwd = &nouser;
                bh.sg_flags = ECHO|CRMOD|ANYP|XTABS;
                stty(0, &bh);
                return(-1);
        }
        pwd = getpwnam(lusername);
        if (pwd == NULL) 
        {
                printf("local user %s not known\n", lusername);
                pwd = &nouser;
                bh.sg_flags = ECHO|CRMOD|ANYP|XTABS;
                stty(0, &bh);
                return(-1);
        }
        rval = ruserok(host, (pwd->pw_uid == 0), rusername, lusername);
        if(rval == -1)
                bh.sg_flags = ECHO|CRMOD|ANYP|XTABS;
        stty(0, &bh);
        return rval == 0;
}

getstr(buf, cnt, err)
        char *buf;
        int cnt;
        char *err;
{
        char c;

        do 
        {
                if (read(0, &c, 1) != 1)
                        exit(1);
                if (--cnt < 0) 
                {
                        printf("%s too long\r\n", err);
                        exit(1);
                }
                *buf++ = c;
        } while (c != 0);
}

char    *speeds[] =
    { "0", "50", "75", "110", "134", "150", "200", "300",
      "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
#define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))

doremoteterm(term, tp)
        char *term;
        struct sgttyb *tp;
{
        char *cp = index(term, '/');
        register int i;

        if (cp) {
                *cp++ = 0;
                for (i = 0; i < NSPEEDS; i++)
                        if (!strcmp(speeds[i], cp)) {
                                tp->sg_ispeed = tp->sg_ospeed = i;
                                break;
                        }
        }
        tp->sg_flags = ECHO|CRMOD|ANYP|XTABS;
}

setenv (var, value)
/*
   sets the value of var to be arg in the Unix 4.2 BSD environment env.
   Var should end with '='.
   (bindings are of the form "var=value")
   This procedure assumes the memory for the first level of environ
   was allocated using malloc.
 */
char *var, *value;
{
        extern char **environ;
        int index = 0;

        while (environ [index] != NULL)
        {
            if (strncmp (environ [index], var, strlen (var)) == 0)
            {
                /* found it */
                environ [index] = (char *) malloc (strlen (var) + strlen (value) + 1);
                strcpy (environ [index], var);
                strcat (environ [index], value);
                return;
            }
            index ++;
        }

        if ((environ = (char **) realloc (environ, sizeof (char *) * (index + 2))) == NULL)
        {
            fprintf (stderr, "Setenv: malloc out of memory\n");
            exit (1);
        }

        environ [index] = (char *) malloc (strlen (var) + strlen (value) + 1);
        strcpy (environ [index], var);
        strcat (environ [index], value);
        environ [++index] = NULL;
}

jmp_buf getcjbuff;

bong()
{
    longjmp(getcjbuff,1);
}

char *
enquire()
{
char buff[256];
int (*oldhandler)();
int i,n;
struct sgttyb h,t;
char *p = "h19-g";

    gtty(fileno(stdin),&h);
    t = h;
    t.sg_flags |= CBREAK  ;
    t.sg_flags &= ~ ( ECHO | RAW) ;
    stty(fileno(stdout),&t);
    if(setjmp(getcjbuff))
    {
        if(setjmp(getcjbuff))
        {
            p = "h19-g";
        }
        else
        {
        alarm(6);
        write(fileno(stdout),"\r\033[0c",5);
        sleep(1);
        n = read(fileno(stdin),buff,256);
        alarm(0);
        signal(SIGALRM, oldhandler);
        for(i = 0; i < n-1; i++)
            if(buff[i] == '\033')
            {
                if(!strncmp(&buff[i+1],"[?1;2c",6))
                {
                    p = "vt100p";
                    break;
                }
                else if(!strncmp(&buff[i+1],"[?62;1;2;6;7;8c",15))
                {
                    p = "vt200";
                    break;
                }
                else if(!strncmp(&buff[i+1],"/K",2))
                {
                    p = "h19-g";
                    break;
                }
                else if(!strncmp(&buff[i+1],"Z",1))
                {
                    p = "h19-g";
                    break;
                }
            }
        }
    }
    else
    {
        oldhandler = signal(SIGALRM, bong);
        alarm(6);
        write(fileno(stdout),"\r\033Z",3);
        sleep(1);
        n = read(fileno(stdin),buff,256);
        alarm(0);
        signal(SIGALRM, oldhandler);
        for(i = 0; i < n-1; i++)
            if(buff[i] == '\033')
            {
                if(!strncmp(&buff[i+1],"[?1;2c",6))
                {
                    p = "vt100p";
                    break;
                }
                else if(!strncmp(&buff[i+1],"[?62;1;2;6;7;8c",15))
                {
                    p = "vt200";
                    break;
                }
                else if(!strncmp(&buff[i+1],"/K",2))
                {
                    p = "h19-g";
                    break;
                }
                else if(!strncmp(&buff[i+1],"Z",1))
                {
                    p = "h19-g";
                    break;
                }
            }
    }
    stty(fileno(stdout),&h);
    printf("Termtype: %s\n",p);
    return p;
}

# ifndef TIOCPKT_WINDOW
# define TIOCPKT_WINDOW 0x80
# endif TIOCPKT_WINDOW

static int Pfd;
struct winsize win = { 0, 0, 0, 0 };
int     cleanup();
int logfd;
char    *line;

exec_sh_on_pty(struct   passwd *pwd)
{
char *c,*d;
int i;
int p;
int on = 1;
int pid;
int t;
char *current_term;
char *namep;
int f = 0; /*use the stdin as the file i/o port*/
int firstc = strlen("/dev/pty");
int secondc = firstc+1;
struct sgttyb tta;
struct tchars ttb;
struct winsize ttc;
struct ltchars ttd;

        ioctl(0, TIOCGETP, &tta);
        ioctl(0, TIOCGETC, &ttb);
        ioctl(0, TIOCGWINSZ, &ttc);
        ioctl(0, TIOCGLTC, &ttd);

        current_term = (char *)ttyname(f);
        
        for (c = "pq"; *c; c++)
        {
        struct stat stb;
                
                line = "/dev/ptyXX";
                line[firstc] = *c;
#ifdef XXXX
                line[secondc] = '0';
                if (access(line, F_OK) < 0)
                        break;
#endif
                for (d = "0123456789abcdef"; *d; d++) 
                {
                        line[secondc] = *d;
                        if(current_term && strcmp(current_term,line))
                        {       
                                p = open(line, 2, 0);
                                if (p > 0)
                                        goto gotpty;
                        }
                }
        }
        printf("Out of ptys\n");
        exit(1);
        /*NOTREACHED*/
gotpty:
        Pfd = p; /*save in Global so we can close it in another function*/
        win = ttc;
        
        line[strlen("/dev/")] = 't';
        t = open(line, O_RDWR, 0600);
        if (t < 0)
        {       /* Can't open the ttypX */
                printf("Can't open %s\n", line);
                exit(1);
        }
                
        pid = fork();
        if (pid < 0)
        {
                printf("Unable to fork\n");
                exit(1);
        }
        
        if (pid == 0) 
        {       /*child*/
        int lastlogfd;
        int utmpfd;
        int wtmpfd;
        int ttyslotnum;
        char *tty;
        int quietlog;

                close(p); /*close the ptyXX side of things*/

                setpgrp(0, 0);
                ioctl(1, TIOCNOTTY, 0);
                
                dup2(t, fileno(stdin));         /* reopen stdout on ttypX */
                dup2(t, fileno(stdout));        /* reopen stdout on ttypX */
                dup2(t, fileno(stderr));        /* reopen stderr on ttypX */

                tta.sg_flags &= ~RAW; 
                tta.sg_flags |= CRMOD | TANDEM | ECHO; 
                ioctl(fileno(stdin), TIOCSETP, &tta);
                ioctl(fileno(stdin), TIOCSETC, &ttb);
                ioctl(fileno(stdin), TIOCSWINSZ, &ttc);
                ioctl(fileno(stdin), TIOCSLTC, &ttd);

                close(t);               /*      Close the ttypX file desc */
                
                current_term = (char *)ttyname(0);
                if ((tty = rindex(current_term, '/')) == NULL)
                        tty = current_term;
                else
                        tty++;

                ttyslotnum = ttyslot();
                if (ttyslotnum > 0 && (utmpfd = open("/etc/utmp", O_WRONLY)) >= 0)
                {
                        lseek(utmpfd, (long)(ttyslotnum*sizeof(utmp)), 0);
                        SCPYN(utmp.ut_line, tty);
                        write(utmpfd, (char *)&utmp, sizeof(utmp));
                        close(utmpfd);
                }
                if ((wtmpfd = open("/usr/adm/wtmp", O_WRONLY|O_APPEND)) >= 0) 
                {
                        write(wtmpfd, (char *)&utmp, sizeof(utmp));
                        close(wtmpfd);
                }
                quietlog = access(qlog, F_OK) == 0;
                if ((lastlogfd = open(lastlog, O_RDWR)) >= 0) 
                {
                        struct lastlog ll;
        
                        lseek(lastlogfd, (long)pwd->pw_uid * sizeof (struct lastlog), 0);
                        if (read(lastlogfd, (char *) &ll, sizeof ll) == sizeof ll &&
                                ll.ll_time != 0 && !quietlog) {
                                printf("Last login: %.*s ",
                                        24-5, (char *)ctime(&ll.ll_time));
                                if (*ll.ll_host != '\0')
                                        printf("from %.*s\n",
                                                sizeof (ll.ll_host), ll.ll_host);
                                else
                                        printf("on %.*s\n",
                                                sizeof (ll.ll_line), ll.ll_line);
                        }
                        lseek(lastlogfd, (long)pwd->pw_uid * sizeof (struct lastlog), 0);
                        time(&ll.ll_time);
                        SCPYN(ll.ll_line, tty);
                        SCPYN(ll.ll_host, utmp.ut_host);
                        write(lastlogfd, (char *) &ll, sizeof ll);
                        close(lastlogfd);
                }
                
                chown(current_term, pwd->pw_uid, tty_group);
                chmod(current_term, 0660);
                setgid(pwd->pw_gid);
                
                strncpy(name, utmp.ut_name, NMAX);
                name[NMAX] = '\0';
                
                initgroups(name, pwd->pw_gid);
                
                quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
                
                if(setuid(pwd->pw_uid) == -1)
                {
                        printf("internal error: Unable to setuid\n");
                        exit(0);
                }
                
                /* destroy environment unless user has asked to preserve it */
                if (pflag == 0) 
                        environ = envinit;
                
                /* set up environment, this time without destruction */
                /* copy the environment before setenving */
                {
                int i = 0;
                char **envnew;
                while (environ [i] != NULL) i++;
        
                        envnew = (char **) malloc (sizeof (char *) * (i + 1));
                        for (; i >= 0; i--)
                                envnew [i] = environ [i];
                        environ = envnew;
                }
        
                setenv("HOME=",pwd->pw_dir);
                setenv("SHELL=",pwd->pw_shell);
                if (term[0] == '\0') strncpy (term,stypeof(tty), sizeof(term));
                setenv("TERM=",term);
                setenv("USER=",pwd->pw_name);
                setenv("PATH=",":/usr/ucb:/bin:/usr/bin");
        
                if ((namep = rindex(pwd->pw_shell, '/')) == NULL)
                        namep = pwd->pw_shell;
                else
                        namep++;
                strcat(minusnam, namep);

                umask(022);
                if (!quietlog) 
                {               /*  */
                        showmotd();
                        strcat(maildir, pwd->pw_name);
                        if (access(maildir, R_OK) == 0) {
                                struct stat statb;
                                stat(maildir, &statb);
                                if (statb.st_size)
                                        printf("You have mail.\n");
                        }
                }
                
                signal(SIGALRM, SIG_DFL);
                signal(SIGQUIT, SIG_DFL);
                signal(SIGINT, SIG_DFL);
                signal(SIGTSTP, SIG_IGN);
                closelog();
                
                execlp(pwd->pw_shell, minusnam, 0);
                
                printf("No shell\n");
                exit(0);
                /*NOTREACHED*/
        }
        close(t);
        /*parent*/
        ioctl(f, FIONBIO, &on);
        {
        struct sgttyb b;
                gtty(f, &b); 
                b.sg_flags = RAW|ANYP; 
                stty(f, &b);
        }
        ioctl(p, FIONBIO, &on);
        ioctl(p, TIOCPKT, &on);
        
        signal(SIGTSTP, SIG_IGN);
        signal(SIGCHLD, cleanup);
        signal(SIGINT, SIG_IGN);
        signal(SIGQUIT, SIG_IGN);

        /*setpgrp(0, 0);*/
        {
        char logbuff[256];
        char hostname[64];
        char *p;
                gethostname(hostname, 64);
                if((p = index(hostname,'.')) != 0)
                        *p = '\0';
                sprintf(logbuff, "/users/staff/X/%s.t%d.%d", hostname, pwd->pw_uid, time(0) );
                logfd = open(logbuff, O_WRONLY | O_CREAT | O_APPEND, 0600);
        }
        protocol(f, p, logfd);
        cleanup();
}

char    magic[2] = { 0377, 0377 };
char    oobdata[] = {TIOCPKT_WINDOW};

/*
 * Handle a "control" request (signaled by magic being present)
 * in the data stream.  For now, we are only willing to handle
 * window size changes.
 */
control(pty, cp, n)
        int pty;
        char *cp;
        int n;
{
        struct winsize w;

        if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
                return (0);
        oobdata[0] &= ~TIOCPKT_WINDOW;  /* we know he heard */
        bcopy(cp+4, (char *)&w, sizeof(w));
        w.ws_row = ntohs(w.ws_row);
        w.ws_col = ntohs(w.ws_col);
        w.ws_xpixel = ntohs(w.ws_xpixel);
        w.ws_ypixel = ntohs(w.ws_ypixel);
        (void)ioctl(pty, TIOCSWINSZ, &w);
        return (4+sizeof (w));
}

/*
 * rlogin "protocol" machine.
 */
protocol(f, p, logfd)
        int f, p, logfd;
{
        char pibuf[1024], fibuf[1024], *pbp, *fbp;
        register pcc = 0, fcc = 0;
        int cc;
        char cntl;

        /*
         * Must ignore SIGTTOU, otherwise we'll stop
         * when we try and set slave pty's window shape
         * (our controlling tty is the master pty).
         */
         
        (void) signal(SIGTTOU, SIG_IGN);
        sleep(1);
        /* send(f, oobdata, 1, MSG_OOB);        /* indicate new rlogin */
        for (;;) 
        {
                int ibits, obits, ebits;

                ibits = 0;      
                obits = 0;
                if (fcc)
                        obits |= (1<<p);
                else
                        ibits |= (1<<f);
                if (pcc >= 0)
                        if (pcc)
                                obits |= (1<<f);
                        else
                                ibits |= (1<<p);
                ebits = (1<<p);
                
                /*LOOK FOR DATA ON ANY OF THE FILEDESCRIPTORS*/
                if (select(16, &ibits, &obits, &ebits, 0) < 0) 
                {       
                        if (errno == EINTR)
                                continue;
                        printf("select %d\n", errno);
                        exit(1);
                }
                
                if (ibits == 0 && obits == 0 && ebits == 0) 
                {
                        /* shouldn't happen... */
                        sleep(5);
                        continue;
                }
                
#define pkcontrol(c)    ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
                
                if (ebits & (1<<p))  /*EXCEPTION ON DESCRIPTOR P*/
                {
                        cc = read(p, &cntl, 1);
                        if (cc == 1 && pkcontrol(cntl)) 
                        {
                                cntl |= oobdata[0];
                                /*send(f, &cntl, 1, MSG_OOB);*/
                                if (cntl & TIOCPKT_FLUSHWRITE) {
                                        pcc = 0;
                                        ibits &= ~(1<<p);
                                }
                        }
                }
                
                if (ibits & (1<<f))     /*DATA COMING IN ON F*/
                {
                        fcc = read(f, fibuf, sizeof (fibuf));
                        if (fcc < 0 && errno == EWOULDBLOCK)
                                fcc = 0;
                        else 
                        {
                        register char *cp;
                        int left, n;

                                if (fcc <= 0)
                                        break;
                                fbp = fibuf;

                        top:
                                for (cp = fibuf; cp < fibuf+fcc-1; cp++)
                                        if (cp[0] == magic[0] &&
                                             cp[1] == magic[1]) 
                                        {
                                                left = fcc - (cp-fibuf);
                                                n = control(p, cp, left);
                                                if (n) 
                                                {
                                                        left -= n;
                                                        if (left > 0)
                                                                bcopy(cp+n, cp, left);
                                                        fcc -= n;
                                                        goto top; /* n^2 */
                                                }
                                        }
                        }
                }

                if ((obits & (1<<p)) && fcc > 0) /*P AVAILABLE TO WRITE TO*/
                {
                        cc = write(p, fbp, fcc);
                        write(logfd, fbp, cc);
                        if (cc > 0) {
                                fcc -= cc;
                                fbp += cc;
                        }
                }

                if (ibits & (1<<p)) /*GOT DATA ON P*/
                {
                        pcc = read(p, pibuf, sizeof (pibuf));
                        pbp = pibuf;
                        if (pcc < 0 && errno == EWOULDBLOCK)
                                pcc = 0;
                        else if (pcc <= 0)
                                break;
                        else if (pibuf[0] == 0)
                                pbp++, pcc--;
                        else 
                        {
                                if (pkcontrol(pibuf[0])) 
                                {
                                        pibuf[0] |= oobdata[0];
                                        /*send(f, &pibuf[0], 1, MSG_OOB);*/
                                }
                                pcc = 0;
                        }
                }
                
                if ((obits & (1<<f)) && pcc > 0) /*CAN WRITE TO F*/
                {
                        cc = write(f, pbp, pcc);
                        write(logfd, pbp, cc);

                        if (cc < 0 && errno == EWOULDBLOCK)
                        {
                                /* also shouldn't happen */
                                sleep(5);
                                continue;
                        }
                        if (cc > 0) 
                        {
                                pcc -= cc;
                                pbp += cc;
                        }
                }
        }
}


cleanup()
{
        rmut();
        signal(SIGHUP, SIG_IGN);
        close(logfd);
        close(Pfd);
        /*vhangup();            /* XXX */
        /*(void) kill(0, SIGKILL); */
        exit(0);
}

struct  utmp wtmp;
char    wtmpf[] = "/usr/adm/wtmp";
char    utmpf[] = "/etc/utmp";
#define SCPYN(a, b)     strncpy(a, b, sizeof(a))
#define SCMPN(a
, b)     strncmp(a, b, sizeof(a))

rmut()
{
register f;
int found = 0;
struct utmp *u, *utmp;
int nutmp;
struct stat statbf;

        f = open(utmpf, O_RDWR);
        if (f >= 0) 
        {
                fstat(f, &statbf);
                utmp = (struct utmp *)malloc(statbf.st_size);
                
                if (!utmp)
                        syslog(LOG_ERR, "utmp malloc failed");
                
                if (statbf.st_size && utmp) 
                {
                        nutmp  = read(f, utmp, statbf.st_size);
                        nutmp /= sizeof(struct utmp);
                
                        for (u = utmp ; u < &utmp[nutmp] ; u++) 
                        {
                                if (SCMPN(u->ut_line, line+5) ||
                                    u->ut_name[0]==0)
                                        continue;
                                lseek(f, ((long)u)-((long)utmp), L_SET);
                                SCPYN(u->ut_name, "");
                                SCPYN(u->ut_host, "");
                                time(&u->ut_time);
                                write(f, (char *)u, sizeof(wtmp));
                                found++;
                        }
                }
                close(f);
        }
        
        if (found) 
        {
                f = open(wtmpf, O_WRONLY|O_APPEND);
                if (f >= 0) 
                {
                        SCPYN(wtmp.ut_line, line+5);
                        SCPYN(wtmp.ut_name, "");
                        SCPYN(wtmp.ut_host, "");
                        time(&wtmp.ut_time);
                        write(f, (char *)&wtmp, sizeof(wtmp));
                        close(f);
                }
        }
        
        chmod(line, 0666);
        chown(line, 0, 0);
        line[strlen("/dev/")] = 'p';
        chmod(line, 0666);
        chown(line, 0, 0);
}

int isremotelogin( char *szHost ) {
    /* TRUE iff utmp host is outside auckland.ac.nz */

    struct hostent *phe;

int i;

    phe = gethostbyname( szHost );

    for( i = 0; i < 4; i++ )
        printf( "%d ", h_addr_list[ 0 ][ i ] );

    return 0;
}
