[comp.sources.misc] v17i076: login - login replacement for Interactive or AT&T V3.2, Part01/01

tony%ajfcal@cpsc.ucalgary.ca (Tony Field) (04/01/91)

Submitted-by: Tony Field <tony%ajfcal@cpsc.ucalgary.ca>
Posting-number: Volume 17, Issue 76
Archive-name: login/part01

Login.c is a replacement for AT&T SysV/386 (or Interactive Unix)
/bin/login.  Most of the features of standard /bin/login are provided
as well as a non-standard "user access control" that may be used
to restrict user access based on dates, times, days of the weeek and
tty line.
 
Tony
------
#! /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 shell archive."
# Contents:  Readme login.1 login.4 Makefile login.c
# Wrapped by ajf@ajfcal on Fri Mar 29 00:18:32 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Readme' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Readme'\"
else
echo shar: Extracting \"'Readme'\" \(2846 characters\)
sed "s/^X//" >'Readme' <<'END_OF_FILE'
X			login.c
X
X/bin/login replacement for AT&T SysV/386 3.2 and Interactive Unix.
X
XAt present, this login cannot be used as a replacement for 
XInteractive's /etc/netlogin: rlogin fails (however telnet works).
X((( can anybody help!!! )))
X
XThis version of login replicates most of the actions of the standard
Xlogin provided with AT&T SysV/386 unix. The standard man page apply, or
Xthe provided man pages may be used.
X
XAT&T and Interactive Unix man pages do not document the /etc/dialups &
X/etc/d_passwd files, however the dialup feature does indeed exist in the
Xdistribution login programme. This and the non-standard "user access
Xsecurity" extension are documented in login.4.
X
XThis version of login supports "user access security". If file
X/etc/usrtime exits, it is processed to restrict the tty line, day of
Xweek, and time of user access. Additional options allow all users, uucp
Xusers and interactive users to be controlled on a global basis. The
Xformat of this file is:
X
X      <user>:<enable>:<tty>:<weekday>:<time>:<comment>
X
Xfor example:
X
X      ajf:LOGIN:/dev/tty01:Mon,Tue:0800-1700:Temp employee
X
X
XSUPPORTED:
X    1.	/etc/passwd, /etc/shadow
X    2.	/etc/default/login
X    3.	/etc/dialups, /etc/d_passwd
X    4.	/etc/utmp, /etc/wtmp
X    5.	/usr/adm/loginlog
X    6.	/etc/ttytype
X    7	.lastlogin
X    8.	/etc/usrtime (non-standard feature)
X
XNOT IMPLEMENTED:
X    1.	what are /usr/lib/uucp/uucico, sublogin, syscon, stty used for?
X	these files are text strings in the original AT&T binary for login
X	but are not implemented in this login routine.
X    2.	internationalization as in Interactive unix.
X    3.	tcp/ip network rlogin support.
X
XDIFFERENCES:
X    1.  TIMEOUT= in /etc/default/login is the total elapsed time required
X        for a successful user name and password.  The AT&T manuals
X        indicate that this really should be the number of seconds
X        between receipt of a userid and the receipt of a password.
X    2.  This login assumes a sane tty condition.
X    3.	CONSOLE= in /etc/default/login accepts colon-separated devices
X    	as well as "?" wildcard specifications.
X    		CONSOLE=/dev/console:/dev/vt??
X    4.	User access security (/etc/usrtime) is not standard.
X    5.	Change of the root directory is implemented as per AT&T man
X    	pages, however, I cannot figure out why the functionality
X    	exits!  No "useful" results could be found.
X    6.	The -p (preserve environment) is not standard for interactive
X    	login (however is a standard feature for tcp/ip login).
X    7.  Of course, other differences exist.....
X    
X
XINSTALL:
X========
XCheck the Makefile before compilation to define your OS and compiler.
X
X
X-----------------------------------------------------------------------------
Xtony field
X
X   uucp: tony@ajfcal.uucp
X	 ..uunet!watmath!calgary!ajfcal!tony
X
Xinternet: tony%ajfcal@cpsc.ucalgary.ca       (I hope!)
END_OF_FILE
if test 2846 -ne `wc -c <'Readme'`; then
    echo shar: \"'Readme'\" unpacked with wrong size!
fi
# end of 'Readme'
fi
if test -f 'login.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'login.1'\"
else
echo shar: Extracting \"'login.1'\" \(4951 characters\)
sed "s/^X//" >'login.1' <<'END_OF_FILE'
X.TH LOGIN 1 ""
X.SH NAME
Xlogin \- sign on user
X
X.SH SYNOPSIS
X
X.B login
X[
X.B -p
X]
X[
X.B name
X[
X.B env-var ...
X]]
X
X where  -p      = perserve environment
X        name    = user login name
X        env-var = environment variable
X.fi
X
X.SH DESCRIPTION
X
XThe 
X.B login
Xcommand allows the user to identify himself to the system.
XIt may be invoked as a command or by the system (usually by
X.B getty
X) when the user first logs in.
X
XIf 
X.B login
Xis invoked as a command, it must replace the initial
Xcommand interpreter by typing:
X
X.nf
X             exec login
X.fi
X
Xfrom the initial (lowest level) shell.
X
XIf a user name is not supplied as a command line argument,
X.B login
Xprompts the user for a user name.  If passwords are required
Xfor the user, then a password is requested.  Echoing is turned
Xoff (if possible) during the entry of the password to ensure that
Xthe password is not displayed on the terminal.
X
XIf five unsuccessful user name and password combinations are
Xattempted, the the erroneous attempts are recorded in
X.B \/usr\/adm\/loginlog
X(if it exits).  Null entries to the login id are not recorded.
XThe terminal is disabled for a period of time
Xafter the unsuccessful attempts.
X
XIf a successful login is not received within 120 seconds,
X.B login
Xsilently disconnects the terminal and returns control to
X.B getty.
X
XAfter successful login, the user\'s user ID, group ID, and login directory
Xare initialized.  The user is informed of the date and time of
Xhis last login (as set in file
X.B \.lastlogin
X).  If the
X.B \.lastlogin
Xfile has been modified by some process other than
X.B login,
Xthe user is warned of a possible breach of his account security.
X
XSubsequently, the user\'s shell (possibly sh(1) or csh(1))
Xis invoked.  As part of the shell startup script (
X.B \.profile
Xor
X.B \.login
X), the message of the day (if any) is printed.
X
XThe basic environment is initialized to include:
X
X.nf
X
X             HOME=user login directory
X             PATH=:/bin:/usr/bin
X             SHELL=shell from /etc/passwd
X             MAIL=/usr/mail/name
X             TZ=timezone spec
X             LOGNAME=usr login name
X             TERM=terminal type
X.fi
X
XAdditional environment entries may be automatically provided by
Xthe shell startup scripts or, if login is executed from the
Xcommand line, by command line options.
X
XCommand line environment options may be of the form:
X
X.nf
X             xxxx
X        or
X             var=yyy
X.fi
X
XIf the option contains an equal sign, then variable and value
Xis placed into the environment as specified.  If the variable
Xis LOGNAME, CDPATH, HOME, PATH, SHELL, MAIL, TZ, HZ or IFS,
Xit is are silently ignored for security reasons.
X
XIf the option does not contain an equal sign, then the option
Xis placed in the environment with the format:
X
X.nf
X		    Ln=xxx
X.fi
X
Xwhere n is a number starting from 0 and is incremented each time
Xa new variable is required.
X
X.SH OPTIONS
X
X.TP
X.B -p
X
XIf this option is not provided,
X.B login
Xinitializes the environment to an empty condition before
Xconstruction of the new users environment.  
X
XIf this option is provided, then the existing environment 
Xis preserved.  Environment values generated during
Xthe login process replace existing values, however other values
Xremain as set in the original environment.
X
X.SH SUBSYSTEM LOGINS
X
XIf the user's shell as specified in
X.B /etc/passwd
Xis "*", then the directory specified as the user's login directory
Xbecomes the root directory (i.e. search paths starting with "/" will
Xstart from this directory), and login is re-executed.  The new
Xroot directory must have it's own
X.B /etc/passwd, /bin/login
Xand
X.B /dev
Xfiles.
X
XThe user is informed that a "Subsystem login" is being performed.
X
X.SH LOGIN DEFAULTS
X
XVarious login default conditions are described in
X.B /etc/default/login.
X
X
X.SH DIALUP PASSWORDS
X
XAuxiliary passwords may be set up for users on selected tty
Xlines to provide additional security with
Xfiles
X.B /etc/dialups
Xand
X.B /etc/d_passwd.
X
X.SH USER ACCESS RESTRICTIONS
X
XIf file
X.B /etc/usrtime
Xexits, it is processed to allow restrictions on
Xaccess for different times of the day, days of
Xthe week, or terminal lines for selected users.
X
X.SH  FILES
X.nf
X /etc/utmp              accounting
X /etc/wtmp              accounting
X /usr/mail/name         user mailbox
X /usr/adm/loginlog      record of failed login attempts
X /etc/passwd            password file
X /etc/shadow            optional shadow password file
X /etc/group             group file
X /etc/profile           system profile (/bin/sh only)
X /etc/dialups           list of dialup tty lines
X /etc/d_passwd          passwords for shells on dialup lines
X /etc/ttytype           default terminal type on tty line
X /etc/default/login     default parameters for login
X /etc/usrtime           time/tty restrictions for user
X \.lastlogin             date of last login
X.fi
X
X.SH SEE ALSO
X
Xlogin(4), mail(1), sh(1), csh(1), bash(1), newgrp(1M), su(1M).
Xloginlog(4), passwd(4), profile(4), environ(5)
END_OF_FILE
if test 4951 -ne `wc -c <'login.1'`; then
    echo shar: \"'login.1'\" unpacked with wrong size!
fi
# end of 'login.1'
fi
if test -f 'login.4' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'login.4'\"
else
echo shar: Extracting \"'login.4'\" \(8995 characters\)
sed "s/^X//" >'login.4' <<'END_OF_FILE'
X.TH LOGIN 4 ""
X.SH NAME
Xlogin: /etc/default/passwd, /etc/usrtime, /etc/d_passwd, /etc/dialups \- login files
X
X.SH DESCRIPTION
X
XA number of files besides
X.B /etc/passwd
Xand 
X.B /etc/shadow
Xare used by the login process to set default user conditions and
Xprovide additional access security.
X
XThe
X.B /etc/default/passwd
Xfile provides default values for the user environment.
X
XThe
X.B /etc/usertime
Xfile provides time, day, and terminal line control on a per-user basis.
X
XThe
X.B /etc/d_passwd
Xand
X.B /etc/dialups
Xfiles allow additional passwords to be associated with tty lines
Xand specific shells.
X
X.SH LOGIN DEFAULT VALUES
X
XVarious
X.B login
Xstartup options may be set in the default control file.
XThese options are of the form:
X
X.nf
X             OPTION=value
X.fi
X
Xand may be placed in
X.B \/etc\/default\/login
Xin any order.  (Comment lines begin with #.)
X
X.TP
X.B TIMEOUT=
Xspecified the total elapsed time in seconds before login silently
Xterminates and returns control to the
X.B getty
Xprocess.   The user is allowed five attempts within this time period
Xto enter a valid login and password sequence.  The default
Xvalue is 120 seconds.
X
X.TP
X.B ULIMIT=
Xspecifies the maximum size (in 512 byte blocks) of file the user 
Xis allowed to create.  No default value is provided.
X
X.TP
X.B TZ=
Xdefines the current time zone, for example, MST7MDT.  The
Xdefault value is EST5EDT.
X
X.TP
X.B HZ=
Xdefines the computers clock tick frequency in Hertz.  No default
Xvalue is provided.
X
X.TP
X.B CONSOLE=
Xsets the allowable terminal devices (from /dev) for a superuser
Xlogin.  If this parameter is not specified, the superuser may
Xlogin from any terminal.  If multiple devices are permitted, they
Xmust be separated by a colon:
X
X.nf
X     CONSOLE=/dev/console:/dev/tty02:/dev/vt01
X.fi
X
XA wild-card match character of "?" is permitted for the
Xdevice names.  The specification of:
X
X.nf
X                /dev/tty??
X.fi
X
Xwill match any tty number with two symbols following the "tty".
X
X.TP
X.B PASSREQ=
Xmay be set to YES to force all users to have passwords or may be
Xset to NO if accounts without passwords are permitted. The default
Xvalue is YES.
X
X.TP
X.B ALTSHELL=
Xmay be set to YES if the login shell\'s name is to be recorded in
Xthe enviornment, or to NO if it is not to be recorded in then
Xenvironment. The default value is YES.
X
X.TP
X.B PATH=
Xmay be set to the default user path.  If this is not provided,
Xthe default path is set to \/bin:\/usr\/bin.
X
X.TP
X.B SUPATH=
Xmay be set to the default path for superuser logins.  If this is
Xnot provided, the default superuser path is \/bin:\/etc:\/usr\/bin.
X
X.TP
X.B UMASK=
Xmay be set to the desired default octal value for umask.  No default
Xvalue is provided.
X
X.TP
X.B IDLEWEEKS=
Xmay be set to the number of weeks before a login is disabled
Xfor lack of use.  By default, this value is not set.
X
X
X.SH DIALUP PASSWORDS
X
XAuxiliary passwords may be set up for users on selected tty
Xlines to provide additional security. The specific tty lines
Xrequiring the additional passwords are defined in
X.B \/etc\/dialups
Xwith one device name per line, eg:
X
X.nf
X      /dev/tty01
X      /dev/vt07
X.fi
X
XThe actual passwords are recorded in
X.B \/etc\/d_passwd
Xwhich has a format similar to that of
X.B \/etc\/passwd. 
XEach password is associated with a specific shell.  
XThe format of the entries is:
X
X.nf
X      <shellname>:<encrypted password>:<comment>
X.fi
X
XIf the shellname is missing, then the line containing the
Xentry is the default for all shells not listed in other
Xlines of the file. If the password is missing, no password is
Xrequired from users of the shell.
X
XA reasonable example would be:
X
X.nf
X    :<encrypted password>:default dialup password
X    /usr/lib/uucp/uucico::UUCP logins don't have extra password
X    /bin/sh:<encrypted password>:normal dialup extra password
X    /bin/csh:<encrypted password>:normal dialup extra password
X.fi
X
XOne useful application of these features is to reserve
Xselected dialin ports for UUCP usage only.  This is done
Xby providing a list of UUCP-only tty lines in
X.B /etc/dialups
Xand setting a password for all shells except uucico.
XThe interactive users calling on the UUCP-only lines
Xwill not know the password.  Since no password is assigned
Xto the uucico shell, the UUCP users have normal access.
X
XNormal password services do not allow construction of the
X.B \/etc\/d_passwd
Xfile. One way to do so is to create a temporary user
Xwith the desired password. With an editor (vi), manually
Xcopy the password from
X.B \/etc\/passwd
Xor
X.B \/etc\/shadow
Xinto the working copy of
X.B \/etc\/d_passwd.
X
XSubsequently, permissions should be set to:
X
X.nf
X      chmod 644 /etc/d_passwd /etc/dialups
X      chown bin /etc/d_passwd /etc/dialups
X      chgrp other /etc/d_passwd /etc/dialups
X.fi
X
X
X.SH USER ACCESS RESTRICTIONS
X
XIf file
X.B /etc/usrtime
Xexits, it is processed to allow restrictions on
Xaccess for different times of the day, days of
Xthe week, or terminal lines for selected users.
X
XComment lines may be in the file: such lines start
Xwith a #.
X
XThe format of this file is
X
X.nf
X      <user>:<enable>:<tty>:<weekday>:<time>:<comment>
X
Xfor example:
X
X      ajf:LOGIN:/dev/tty01:Mon,Tue:0800-1700:Temp employee
X.fi
X
X
X.TP
X.B user
Xis a comma-separated list of user names that is to be restricted. 
XThe names should be present in
X.B /etc/passwd.
X
X.nf
XFor example:   ajf,guest,bill:
X.fi
X
XIf the
X.B user
Xfield is empty, then the line becomes a default for all users
Xnot specifically mentioned in the remaining part of the file.
X
XThree special values ("ALL", "UUCP",
Xor "INTERACTIVE")  may be optionally used in the
X.B
Xuser
Xfield.  The restrictions defined in
Xthese lines apply in a global sense.  Not only is a user restricted to
Xlog in as defined by his private line entry, he is further restricted
Xby the definitions in the "ALL" and/or "INTERACTIVE" line.
X.B UUCP
Xusers (i.e. those users with a login shell of
X.B /usr/lib/uucp/uucico
X) are also restricted by the parameters in the "UUCP" and "ALL" lines.
XSuch lines are normally placed at the beginning of the file.
X
XThe root user is never restricted. If other
Xusers need guaranteed access (e.g. other administrative
Xaccounts), they should be described in the file
Xbefore the "ALL", "UUCP" and "INTERACTIVE" users.  Such users
Xare normally unrestricted:
X
X.nf
X        sys,bin,adm,powerdown,sysadm:::::
X.fi
X
XIf a user is not mentioned in
X.B /etc/usrtime,
Xthen the user is not subject to time restrictions unless
Xa default user, "ALL", "INTERACTIVE" or "UUCP" entries
Xare defined.
X
X.TP
X.B enable
Xdetermines if the user's login is enabled or disabled.
XA login is disabled with "NOLOGIN".  Login may be enabled with "LOGIN"
Xor an empty field.
X
XIf the
X.B enable
Xfield contains date ranges of the form:
X
X.nf
X                 yyyymmdd-yyyymmdd
X
Xfor example:    :19901201-19910308:
X.fi
X
Xthen user logins are accepted only over the stipulated date range or
Xcomma-separted date ranges.
X
XIf the key word "NOLOGIN"
Xis present for "ALL", "UUCP", or "INTERACTIVE" users, then
Xlogin is disabled appropriately without further processing
Xof user names.
X
X.TP
X.B tty
Xis a list of permitted devices the user may access.  More than
Xone device must be separated by commas.  If
X.B tty
Xis empty, then the user may log in on all devices.
X
X.nf
XFor example:    :/dev/tty01,/dev/tty02:
X.fi
X
XA wild-card match character of "?" is permitted for the
Xdevice names.  The specification of:
X
X.nf
X                :/dev/tty??:
X.fi
X
Xwill match any tty number with two symbols following the "tty".
X
X.TP
X.B weekday
Xis a list of days that the user is permitted to log in. The list
Xof days is comma-separated and chosen from:
X
X.nf
X          Mon, Tue, Wed, Thu, Fri, Sat, Sun
X
Xfor example:    :Mon,Wed,Fri:
X.fi
X
XIf the
X.B weekday
Xfield is empty, then no day restrictions are imposed.
X
X.TP
X.B time
Xis a list of comma-separated time ranges that the user is permitted
Xto log in.  A time range is of the form:
X
X.nf
X                 hhmm-hhmm
X
Xfor example:    :0800-1200,1300-1700:
X.fi
X
XIf the
X.B time
Xfield is empty, then no time restrictions are imposed.
X
X.TP
X.B comment
Xis arbitrary commentary that is ignored.
X
X.SH NOTES
XFor security reasons, the permissions on
X.B /etc/usrtime
Xshould be set to:
X
X.nf
X         chown root /etc/usrtime
X         chgrp sys /etc/usrtime
X         chmod 400 /etc/usrtime
X.fi
X
XAn example of a
X.B  usrtime
Xfile is:
X
X.nf
X       # <user>:<enable>:<tty>:<weekday>:<time>:<comment>
X       sys,bin,adm,powerdown,sysadm:::::
X       operator:::::
X       ALL::::0400-2200:midnite shift for backups only
X       INTERACTIVE:::Mon,Tue,Wed,Thu,Fri:0630-1830:
X       UUCP:::::
X       ::::0800-1700:default for anybody not mentioned below
X       bill,loris:LOGIN:/dev/ttyi1a::0830-1630:accounting
X       anil,byron:LOGIN:/dev/tty???:::engineering
X       harry:NOLOGIN:/dev/tty???:::on holidays
X       aubry:19910401-19910431::::contract programmer
X.fi
X
X.SH LIMITS
XLines in
X.B /etc/usrtime
Xare limited to 500 characters in length. Unpredictable
Xresults may be expected if this limit is exceeded.
X   
X.SH SEE ALSO
X
Xlogin(1), passwd(4), loginlog(4), utmp(4)
END_OF_FILE
if test 8995 -ne `wc -c <'login.4'`; then
    echo shar: \"'login.4'\" unpacked with wrong size!
fi
# end of 'login.4'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(1371 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#		login  --  for AT&T unix SysV / 3.2.2
X
X# Set operating system.
X#
X#	-DATT for AT&T unix 3.2.2
X#	-DIAC for Interactive 2.2
X
X# OS=-DATT
XOS=-DIAC
X
X# Set UA=-DUSRACCESS if user access time feature is used (see login.4)
X#     UA=            if not
X
XUA=-DUSRACCESS
X# UA=
X
X# Set -DNOISY if you want unsuccessful login attempts to be
X#	reported to the user. Otherwise, login is silent
X#	about failures.  Standard login reports unsuccessful attempts.
X
X# QUIET=-DNOISY
XQUIET=
X
X# Set PG=-DPOSIX_GROUPS if supplimentary groups are used (not for AT&T)
X# Set LIBS to ensure that initgroups() and alloca() are available
X# TCP/IP connection on Interactive need -linet
X# Usually, initgroups() in -lcposix     (ATT unix does not have this)
X#              alloca() in -lPW (if GCC is used, don't define -lPW)
X
XPG=-DPOSIX_GROUPS
X# LIBS=-lPW
XLIBS=-lcposix -linet
X# PG=
X# LIBS=-lPW
X
X# Set compiler
X
XCC=gcc -traditional
X# CC=cc
X
XCFLAGS=	-O $(OS) $(QUIET) $(PG) $(UA)
XSRC=	login.c
X
X
Xall: login
X
Xlogin: login.c
X	${CC} $(CFLAGS) -o $@ $@.c $(LIBS)
X
Xtar:
X	tar cvf login.tar Readme login.1 login.4 Makefile $(SRC)
X
Xshar:
X	shar Readme login.1 login.4 Makefile $(SRC) > login.shar
X
Xclean:
X	rm -f ${OBJS} core login udump *.B *~
X
Xinstall:
X	install -f /bin -u root -g bin -m 4755 login
X
X#	install -f /usr/man/man.1 -u bin -g bin -m 644 login.1
X#	install -f /usr/man/man.4 -u bin -g bin -m 644 login.4
END_OF_FILE
if test 1371 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'login.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'login.c'\"
else
echo shar: Extracting \"'login.c'\" \(30942 characters\)
sed "s/^X//" >'login.c' <<'END_OF_FILE'
X/* ta=4     tabs are set to 4.  vi: "set ts=4" */
X/****************************************************************************
X*                   l o g i n . c                                          	*
X*																			*
X* tony field																*
X*      uucp: tony@ajfcal.uucp												*
X*            ..uunet!watmath!calgary!ajfcal!tony							*
X*  internet: tony%ajfcal@cpsc.ucalgary.ca									*
X****************************************************************************/
X
X/*	login replacement for AT&T SysV/386 3.2 and Interactive Unix.
X
X	Replicate most (at least many) of the actions of the standard 
X	/bin/login provided with AT&T unix.  User access security is added.
X	The standard man pages apply except for /etc/usrtime.
X
XInteractive & AT&T /etc/utmp entries are different!  (why??)
X	Interactive contains devices as:
X		root       console
X		LOGIN      /dev/vt02  	<---
X		ajf        ttyp0      
X	AT&T contains devices as:
X		root       console    
X		LOGIN      vt02  		<---
X		ajf        ttyp0      
X
X	The device comparison for Interactive must be able to
X	handle /dev/xxx or xxx format in utmp - seems to be
X	related only to /dev/vt?? devices.
X
XNOTE: 	does not yet work as a replacement for /etc/netlogin.
X		It seems to work for telnet access but not rlogin access.
X
X		called by telnet:   login -p
X		called by rlogin:   login -r <systemid>
X		called by getty:    login <username>
X	
X			-p = preserve environment
X			-r = invoked by rlogind
X
XREVISIONS:
X==========
X	1991-Mar-06:  Interactive V 2.2 conversion.
X		1. add posix group support - initgroups()
X		2. correct /etc/utmp differences AT&T vs. Interactive
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X#ifdef IAC
X#include <utime.h>
X#endif
X#include <sys/stat.h>
X#include <unistd.h>
X#include <signal.h>
X
X#include <termio.h>
X
X#include <sys/ioctl.h>
X#include <fcntl.h>
X#include <string.h>
X#include <time.h>
X#include <utmp.h>
X#include <pwd.h>
X
X#ifdef __GNUC__
X#define alloca __builtin_alloca
X#else /* not __GNUC__ */
Xchar *alloca ();
X#endif
X
X#define	TIMEOUT			120					/* 	seconds before login aborted*/
X#define MAXBAD			5					/*	max allowed login attempts 	*/
X#define BADLOGINTIME	200					/*	wait time before allowing new logins */
X#define	MAXUSERNAME		25					/*	size of user name			*/
X
X#define DEFAULTSHELL	"/bin/sh"
X#define USERPATH		"/bin:/usr/bin"
X#define ROOTPATH		"/bin:/etc:/usr/bin"
X#define MAIL			"/usr/mail"
X#define DEFAULTTZ		"EST5EDT"
X
X#define BINPASSWD		"/bin/passwd"
X
X#define DIALUPTTY		"/etc/dialups"
X#define DIALUPPWD		"/etc/d_passwd"
X#define SHADOW			"/etc/shadow"
X#define LASTLOGIN		".lastlogin"
X#define DEFAULTFILE		"/etc/default/login"
X#define LOGINLOG		"/usr/adm/loginlog"
X#define TTYTYPE			"/etc/ttytype"
X#define	WTMP			"/etc/wtmp"
X#define	USRTIME			"/etc/usrtime"
X
X#define	ROOT			0
X
X/*	user is forbidden to modify these with login command line options */
Xchar *forbidden[] = {"HOME=", "PATH=",   "SHELL=",  "MAIL=",  "IFS=",
X					 "HZ=",   "CDPATH=", "LOGNAME=", NULL };
X
X#define MAXPATHLEN		300
X#define LONGLINE		500
X#define SHORTLINE		100
X
X#ifdef ATT
Xstruct utimbuf			/*	missing from /usr/include	*/
X{	time_t	actime;		/*	access time					*/
X	time_t	modtime;	/*	modification time			*/
X} ;
X#endif
X
X#ifdef USRACCESS
Xstruct usrtime			/* fields within /etc/usrtime	*/
X{	char	*enable;
X	char	*ttyline;
X	char	*days;
X	char	*timerange;
X} ;
X#endif
X
Xlong	shadow_lstchg, shadow_min, shadow_max;
X
X#ifdef __STDC__
Xint main(int argc, char **argv);
Xint dialup_pwd(char *ttyfname, char *pwdfname, char *tty_name, struct passwd *pwd);
X#ifdef USRACCESS
Xint get_usrtime(char *fname, char *user, char *shell, char *tty);
Xint check_user(long *now, char *tty, struct usrtime *usr);
Xint check_tty(char *tty, char *list);
Xint check_day(long now, char *list);
Xint check_time(long now, char *list, int timerange);
X#endif
Xvoid setenv(char *this, char *val);
Xvoid check_putenv(char *val);
Xchar *get_shadow(char *fname, char *user, int allow);
Xchar *search_ttytype(char *tty);
Xint get_defaults(char *fname, char *tz, char *hz, char **console, long *ulim, int *passreq, int *altshell, char **path, char **supath, long *timeout, int *cmask, int *idleweeks);
Xvoid trim(char *s);
Xint atoo(char *s);
Xvoid user_timeout(void);
Xint tscan(char s[], char t[]);
Xint qtscan(char s[], char t[]);
X#else
Xint main();
Xint dialup_pwd();
X#ifdef USRACCESS
Xint get_usrtime();
Xint check_user();
Xint check_tty();
Xint check_day();
Xint check_time();
X#endif
Xvoid setenv();
Xvoid check_putenv();
Xchar *get_shadow();
Xchar *search_ttytype();
Xint get_defaults();
Xvoid trim();
Xint atoo();
Xvoid user_timeout();
Xint tscan();
Xint qtscan();
X#endif
X
X
Xextern char *getpass(), *crypt(), *malloc();
Xlong	timeout;				/*	TIMEOUT from /etc/default/login		*/
Xlong	atol();
Xint		atoi();
X
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X	char *login_name;							/* user login id  		*/
X	char user_name[SHORTLINE+1];
X	char *tty_name, *tty_n, *ttyname();			/* tty name for user	*/
X	int	 i;
X	struct passwd *getnam();
X	struct passwd *pwd;
X	char	shell[MAXPATHLEN + 1];
X	char	term[SHORTLINE + 1];
X	char	buffer[MAXPATHLEN+1], *p;
X	char	*shadow, *my_pass;
X	char	*ctime();
X	long	ulimit_blocks;					/*	ULIMIT from /etc/default/login */
X	char	tz[50];							/*	TZ			"			*/
X	char	hz[50];							/*	HZ			"			*/
X	char	*console;						/*	CONSOLE		"			*/
X	int		passreq;						/*	PASSREQ		"			*/
X	int		altshell;						/*	ALTSHELL	"			*/
X	char	*path;							/*	PATH		"			*/
X	char	*supath;						/*	SUPATH		"			*/
X	int		cmask;							/*	UMASK		" 			*/
X	int		idleweeks;						/*	IDLEWEEKS	"			*/
X	int		passwd_needed = 0;
X	int		null_user_count;
X	int		lcreate;
X	char	bad_user[MAXBAD][MAXUSERNAME+1];
X	extern char	**environ, *optarg;
X	extern int  optind;
X	int		c;
X	char	*domain, *hostname, *environ_init[1];
X	int		preserve = 0;
X
X
X#ifndef NETWORK
X	/*	this is valid only for /bin/login. */
X	if (getppid () != 1)
X	{	fprintf (stderr, "login must be 'exec'ed from lowest level shell.\n");
X		exit (1);
X	}
X#endif
X
X#if defined (IAC)  &&  defined (NETWORK)
X	gethostname(buffer, sizeof(buffer));
X	domain = strchr (buffer, '.');
X	hostname = NULL;
X#endif
X
X	while ((c = getopt(argc, argv, "pr:")) != -1)
X	{
X		switch (c)
X		{
X		case 'p':	/*	preserve environment  */
X					preserve = 1;
X					break;
X#if defined (IAC)  &&  defined (NETWORK)
X		case 'r':	/*	rlogind initiated this call, remote host name */
X					if (getuid())
X					{	fprintf (stderr, "%s: -r for superuser only\n", argv[0]);
X						exit (1);
X					}
X					if (domain && (p = strchr (optarg, '.')) &&
X					    strcasecmp(p, domain) == 0)
X						*p = 0;
X					hostname = optarg;
X					break;
X#endif
X		default:	break;
X		}
X	}
X
X	console = path = supath = NULL;
X	*tz = *hz = '\0';
X	idleweeks = timeout = cmask = -1;
X	ulimit_blocks = 0;
X	passreq = 1;
X	altshell = 1;
X	shadow_lstchg = -1;
X
X	signal (SIGALRM, user_timeout);
X	alarm  (TIMEOUT);
X	signal (SIGQUIT, SIG_IGN);
X	signal (SIGINT, SIG_IGN);
X
X	if (optind < argc)
X		login_name = argv[optind++];
X	else
X		login_name = NULL;
X
X	tty_name = ttyname (0);
X	if (tty_name == NULL  ||  *tty_name == '\0')
X		tty_name = "/dev/tty??";
X	if ((tty_n = strrchr (tty_name, '/')))
X		tty_n++;
X	else
X		tty_n = tty_name;
X
X	/*	get defaults from /etc/default/login	*/
X	
X	get_defaults (DEFAULTFILE, tz, hz, &console, &ulimit_blocks, &passreq, 
X				  &altshell, &path, &supath, &timeout, &cmask, &idleweeks);
X	if (timeout != -1)
X		alarm (timeout);
X	else
X		timeout = TIMEOUT;
X	if (path == NULL)
X		path = USERPATH;
X	if (supath == NULL)
X		supath = ROOTPATH;
X	if (*tz == '\0')
X		strcpy (tz, DEFAULTTZ);
X
X	/*	accept a user's login user name and password	*/
X
X	for (null_user_count = i = 0;  i < MAXBAD;  i++)
X	{	char	*salt, *pass, *pass_crypt;
X		if (login_name)
X		{	strcpy (user_name, login_name);
X			login_name = NULL;
X		}
X		else
X		{	fprintf (stdout, "login: ");
X			fgets (user_name, SHORTLINE, stdin);
X			user_name[SHORTLINE] = 0;
X			trim (user_name);
X			user_name[MAXUSERNAME] = '\0';
X			strcpy (bad_user[i], user_name);
X			if (*user_name == '\0')
X			{	null_user_count++;
X				continue;
X			}
X		}
X		if ((pwd = getpwnam (user_name)))
X		{	if (*(pwd->pw_shell) == '*')
X			{	/*	see 'real' man page for login for * in shell field.
X					must change the root directory, then rerun login
X					with the new root looking at new password file...
X					HOWEVER, this does not work as I expected.
X				*/
X				if (chroot (pwd->pw_dir))
X				{	perror ("Could not change to your root directory: ");
X					exit (1);
X				}
X				fprintf (stderr, "Subsystem root = %s\n", pwd->pw_dir);
X				execlp ("login", "login", 0);
X				perror ("No login on subsystem: ");
X				exit (1);
X			}
X
X			/*	valid user.  shadow file may or may not exist */
X			if ((strcmp (pwd->pw_passwd, "x") == 0)  &&  (shadow = get_shadow (SHADOW, user_name, 0)))
X			{	salt = shadow;
X				my_pass = shadow;
X			}
X			else 
X				my_pass = salt = pwd->pw_passwd;
X		}
X		else /* invalid user */
X			my_pass = salt = "xx";
X
X		if (*my_pass == '\0')
X		{	/*	bypass prompting if zero length */
X			if (passreq)
X				passwd_needed = 1;		/*	ask user later */
X#ifdef USRACCESS
X			if (get_usrtime (USRTIME, user_name, pwd->pw_shell, tty_name))
X				exit (1);
X#endif
X			break;
X		}
X
X		pass = getpass ("password: ");
X		if (pwd)
X		{	/*	get regular + dialup password + restrict time access */
X			pass_crypt = crypt (pass, salt);
X			if ( !strcmp (pass_crypt, my_pass)  
X					&&  !dialup_pwd (DIALUPTTY, DIALUPPWD, tty_name, pwd)
X#ifdef USRACCESS
X					&&  !get_usrtime (USRTIME, user_name, pwd->pw_shell, tty_name)
X#endif
X				)
X				break;
X		}
X#ifdef NOISY
X		if (i > 1)
X			sleep (i);
X		fprintf (stdout, "Login incorrect: try again.\n");
X#endif
X	}
X	alarm (0);						/*	turn of pending alarm	*/
X
X	if (i == MAXBAD)
X	{	/*	write the bad attempt to /usr/adm/loginlog. */
X		FILE *fp;
X		time_t	now;
X
X		if (null_user_count < 3)
X		{	if ((fp = fopen (LOGINLOG, "a")))
X			{	now = time (0L);
X				for (i = 0;  i < MAXBAD;  i++)
X				{	if (*bad_user[i])
X					fprintf (fp, "%s:%s: %s", bad_user[i], tty_name, ctime (&now));
X				}
X				fclose (fp);
X			}
X			sleep (BADLOGINTIME);		/* discourage further abuse */
X		}
X		exit (1);
X	}
X
X	/*	if CONSOLE= is defined in /etc/default/login, then root may
X		root may log in only on the CONSOLE=device
X	*/
X	if (pwd->pw_uid == ROOT  &&  console  &&  *console)
X	{	strcpy (buffer, tty_name);
X		strcat (buffer, ":");
X		if (qtscan (console, buffer) < 0)
X		{	fprintf (stderr, "Superuser must be on %s\n", console);
X			exit (1);
X		}
X	}
X
X	if (*(pwd->pw_shell) == '\0')
X		strcpy (shell, DEFAULTSHELL);
X	else
X		strcpy (shell, pwd->pw_shell);
X
X	if (chdir (pwd->pw_dir) < 0)
X	{	perror ("Could not locate login directory");
X		exit (1);
X	}
X
X	{	/*	update utmp and wtmp (if wtmp exists)
X			For an original tty login, utmp will contain:
X				LOGIN   /dev/vt01
X			For exec login:
X				ajf		vt01
X			Must scan for either getutline() form.
X		*/
X		struct utmp *utmp;
X		struct utmp line, id;
X		struct utmp *getutline();
X
X#ifdef IAC
X		/* first check for /dev/tty?? format */
X
X		memset ((char *)&line, '\0', sizeof(line));
X		strncpy (line.ut_line, tty_name, sizeof(line.ut_line));
X		if ((utmp = getutline (&line)) == NULL)
X		{
X			/*	for tty?? format */
X			setutent();
X			memset ((char *)&line, '\0', sizeof(line));
X			strncpy (line.ut_line, tty_n, sizeof(line.ut_line));
X			if ((utmp = getutline (&line)) == NULL)
X			{	fprintf (stderr, "No utmp entry.\n");
X				exit (1);
X			}
X		}
X#else
X		memset ((char *)&line, '\0', sizeof(line));
X		strncpy (line.ut_line, tty_n, sizeof(line.ut_line));
X		if ((utmp = getutline (&line)) == NULL)
X		{
X			fprintf (stderr, "No utmp entry.\n");
X			exit (1);
X		}
X#endif
X		memset ((char *)&id, '\0', sizeof(id));
X		strncpy (id.ut_id, utmp->ut_id, sizeof(id.ut_id));
X		(void) time (&id.ut_time);
X		strncpy (id.ut_name, user_name, sizeof(id.ut_name));
X		strncpy (id.ut_line, tty_n, sizeof(id.ut_line));
X		id.ut_pid = getpid();
X		id.ut_type = USER_PROCESS;
X		pututline (&id);
X
X		if (access (WTMP, W_OK) == 0)
X		{	FILE	*wtmp;
X			if ((wtmp = fopen (WTMP, "a")))
X			{	fwrite (&id, sizeof(id), 1, wtmp);
X				fclose (wtmp);
X			}
X		}
X	}
X
X	if (pwd->pw_uid != ROOT  &&  ulimit_blocks)
X	{	if (ulimit (2, ulimit_blocks) == -1)
X			fprintf (stderr, "Could not set ULIMIT=%ld\n", ulimit_blocks);
X	}
X
X	/*	set user context: uid/gid and environment */
X
X	if (setgid (pwd->pw_gid))
X	{	perror ("Could not set gid=%d\n", pwd->pw_gid);
X		exit (1);
X	}
X#ifdef POSIX_GROUPS
X	initgroups (user_name, pwd->pw_gid);	/* posix supplimentary groups */
X#endif
X	if (setuid (pwd->pw_uid))
X	{	perror ("Could not set uid=%d\n", pwd->pw_uid);
X		exit (1);
X	}
X
X	if (passwd_needed)
X	{	fprintf (stdout, "A password is needed to log into this system. Choose one.\n");
X		if (system (BINPASSWD))
X			exit (1);
X	}
X	else if (shadow_lstchg >= 0)
X	{	if ((time(NULL) / (24L*60*60) - shadow_lstchg) > shadow_max)
X		{	fprintf (stdout, "Your password has expired. Choose a new one.\n");
X			if (system (BINPASSWD))
X				exit (1);
X		}
X	}
X
X	/*	lastlogin magic seems to use a 2 second difference in
X		the modified and access times of the .lastlogin file.
X		This is a guess....but it seems to work.
X	*/
X	sprintf (buffer, "%s/%s", pwd->pw_dir, LASTLOGIN);
X	if (access (buffer, 0))
X	{	FILE	*fp;
X		fprintf (stdout, "Warning: creating .lastlogin\n");
X		lcreate = 1;
X		if (fp = fopen (buffer, "w"))
X		{	fclose (fp);
X			chmod (buffer, 0400);
X		}
X	}
X	else
X		lcreate = 0;
X	if (access (buffer, 0) == 0)
X	{	struct stat sb;
X		struct utimbuf timset;
X		time_t	now;
X
X		if (stat (buffer, &sb) == 0)
X		{	if (lcreate == 0)
X			{	if (sb.st_mtime - sb.st_atime < 2)
X					fprintf (stdout, "Warning: .lastlogin has been modified since your previous login.\n");
X				fprintf (stdout, "last login: %s", ctime (&sb.st_mtime));
X			}
X			now = time (0L);
X			if (idleweeks > 0  &&  pwd->pw_uid != ROOT
X			    &&  ((now - sb.st_atime) / (24L * 60 * 60 * 7 * (long) idleweeks)) >= idleweeks)
X			{	fprintf (stderr, "Login disabled due to lack of usage.\n");
X				exit (1);
X			}			
X			timset.actime  = now - 2; 
X			timset.modtime = now;
X			utime (buffer, &timset);
X		}
X	}
X	else
X		fprintf (stderr, "Warning: could not find .lastlogin\n");
X
X	if (cmask != -1)
X		umask (cmask);
X
X	if (!preserve)
X	{	environ_init[0] = NULL;
X		environ = environ_init;
X		strncpy(term, search_ttytype(tty_n), sizeof(term));
X		term[sizeof(term) - 1] = 0;
X		setenv ("TERM",    term);
X	}
X	sprintf (buffer, "%s/%s", MAIL, user_name);
X	setenv ("MAIL",    buffer);
X	setenv ("HOME",    pwd->pw_dir);
X	setenv ("LOGNAME", user_name);
X	if (altshell)
X		setenv ("SHELL",   shell);
X	if (pwd->pw_uid == ROOT)
X		setenv ("PATH", supath);
X	else
X		setenv ("PATH", path);
X	if (*tz)
X		setenv ("TZ", tz);
X
X	for (i = optind;  i < argc;  i++)		/*	environment from command line */
X		check_putenv (argv[i]);
X	
X	signal (SIGALRM, SIG_DFL);
X	signal (SIGQUIT, SIG_DFL);
X	signal (SIGINT,  SIG_DFL);
X#ifdef SIGTSTP
X	/*	Interactive has job control signals, AT&T does not */
X	signal (SIGTSTP, SIG_IGN);
X#endif
X
X	/*	exec the desired shell */
X
X	buffer[0] = '-';
X	strcpy (buffer + 1, (p = strrchr (shell, '/')) ? p + 1 : shell);
X	execlp (shell, buffer, 0);
X	fprintf (stderr, "login: no shell: ");
X	perror (shell);
X	exit (1);
X}
X
X/****************************************************************************
X*	read /etc/dialups & /etc/d_passwd, if they exist						*
X*	Return 0 if user has access, 1 if no access								*
X****************************************************************************/
X
Xint dialup_pwd (ttyfname, pwdfname, tty_name, pwd)
Xchar	*ttyfname;		/*	/etc/dialups									*/
Xchar	*pwdfname;		/*	/etc/d_passwd									*/
Xchar	*tty_name;		/*	user's tty line									*/
Xstruct passwd *pwd;		/*	user;s /etc/passwd file entry					*/
X{	FILE	*fp;
X	char	dtty[SHORTLINE+1];
X	char	*dpass, *dcrypt, *salt, *shell, *dialpass, *crypt();
X
X	if (pwd == NULL)
X		return (0);
X
X	if (*(pwd->pw_shell))
X		shell = pwd->pw_shell;
X	else
X		shell = DEFAULTSHELL;
X	if (access (ttyfname, 0))
X		return (0);
X	if (fp = fopen (ttyfname, "r"))
X	{	while (fgets (dtty, SHORTLINE, fp))
X		{	dtty[SHORTLINE] = 0;
X			trim (dtty);
X			if (strcmp (dtty, tty_name) == 0)
X			{	fclose (fp);
X				if (dialpass = get_shadow (DIALUPPWD, shell, 1))
X				{	if (*dialpass == '\0')		/*	no password needed */
X						return (0);
X					salt = dialpass;
X				}
X				else 
X					return (1);
X				dpass = getpass ("dialup password: ");
X				dcrypt = crypt (dpass, salt);
X				if (strcmp (dcrypt, dialpass))
X					return (1);
X				else
X					return (0);
X			}
X		}
X		fclose (fp);
X	}
X	return (0);
X}
X
X#ifdef USRACCESS
X/****************************************************************************
X*	process /etc/usrtime, if it exists										*
X*	Return 0 if user has access, 1 if no access								*
X****************************************************************************/
X
Xint get_usrtime (fname, user, shell, tty)
Xchar	*fname;			/*	/etc/usrtime									*/
Xchar	*user;			/*	user login name									*/
Xchar	*shell;			/*	user's shell (from /etc/passwd)					*/
Xchar	*tty;			/*	user's login tty line							*/
X{
X	struct usrtime	all, uucp, interact, genuser, thisuser, *which;
X	char	line[LONGLINE+1];
X	FILE	*fp;
X	char	*var[5], *colon;
X	char	*uname, save_letter;
X	int		failure, i;
X	long	now;
X	char	*uucico;
X
X	if (strcmp (user, "root") == 0)
X		return (0);
X
X	uucico = "uucico";
X	all.enable = uucp.enable = interact.enable
X			   = thisuser.enable = genuser.enable = NULL;
X	failure = 0;
X	uname = alloca (strlen (user) + 3);
X	strcpy (uname, user);
X	strcat (uname, ",");
X	if ((fp = fopen (fname, "r")))
X	{	
X		while (fgets (line, LONGLINE, fp))
X		{	line[LONGLINE] = '\0';
X			if (*line == '#')
X				continue;
X			trim (line);
X			for (colon = line, i = 0;  i < 5;  i++)
X			{	var[i] = colon;
X				colon = strchr (colon, ':');
X				if (colon == NULL)
X					break;
X				*colon++ = '\0';
X			}
X			if (colon == NULL)					/*	need min of 5 colons */
X				continue;
X			which = NULL;
X
X			/*	select first instance of ALL, UUCP, INTERACTIVE  or default */
X			if (strcmp (var[0], "ALL") == 0)
X			{	if (strcmp (var[1], "NOLOGIN") == 0)
X				{	failure = 1;
X					break;
X				}
X				if (all.enable)
X					continue;
X				which = &all;
X			}
X			else if (strcmp (var[0], "UUCP") == 0)
X			{	if (strcmp (var[1], "NOLOGIN") == 0  &&  tscan (shell, uucico) >= 0)
X				{	failure = 1;
X					break;
X				}
X				if (uucp.enable)
X					continue;
X				which = &uucp;
X			}
X			else if (strcmp (var[0], "INTERACTIVE") == 0)
X			{	if (strcmp (var[1], "NOLOGIN") == 0  &&  tscan (shell, uucico) < 0)
X				{	failure = 1;
X					break;
X				}
X				if (interact.enable)
X					continue;
X				which = &interact;
X			}
X			else if (var[0][0] == '\0')
X			{	/*	general default user setup */
X				if (genuser.enable)
X					continue;
X				which = &genuser;
X			}
X
X			if (which)
X			{	/*	process ALL, UUCP, INTERACTIVE or default user lines */
X				which->enable 	 = alloca (strlen (var[1]) + 2);
X				which->ttyline   = alloca (strlen (var[2]) + 2);
X				which->days    	 = alloca (strlen (var[3]) + 2);
X				which->timerange = alloca (strlen (var[4]) + 2);
X				strcpy (which->enable, 	  var[1]);
X				strcpy (which->ttyline,   var[2]);
X				strcpy (which->days,      var[3]);
X				strcpy (which->timerange, var[4]);
X			}
X			else
X			{	/*	standard user line */
X				i = strlen (var[0]);
X				save_letter = var[1][0];
X				strcat (var[0], ",");
X				if (tscan (var[0], uname) >= 0)
X				{	var[0][i] = '\0';				/*	undo comma */
X					var[1][0] = save_letter;
X					thisuser.enable    = var[1];
X					thisuser.ttyline   = var[2];
X					thisuser.days      = var[3];
X					thisuser.timerange = var[4];
X					if (strcmp (var[1], "NOLOGIN") == 0)
X						failure = 1;
X					break;
X				}
X			}
X		}
X	 	fclose (fp);
X		if (failure || (genuser.enable  &&  thisuser.enable == NULL  && strcmp (genuser.enable, "NOLOGIN") == 0))
X		{	fprintf (stderr, "Logins are temporarily disabled.\n");
X			return (2);
X		}
X		now = time (NULL);
X		if (   (all.enable      && check_user (now, tty, &all))
X			|| (uucp.enable     && tscan (shell, uucico) >= 0 && check_user (now, tty, &uucp))
X			|| (interact.enable && tscan (tty, uucico) < 0 && check_user (now, tty, &interact))
X			|| (genuser.enable  && thisuser.enable == NULL && check_user (now, tty, &genuser))
X			|| (thisuser.enable && check_user (now, tty, &thisuser)))
X			return (1);
X	}
X	return (0);
X}
X
X/****************************************************************************
X*	usrtime: check fields in sequence to see if user has access permission	*
X****************************************************************************/
X
Xcheck_user (now, tty, usr)
Xchar	*tty;			/*	user's tty line									*/
Xlong	*now;			/*	current time of day (now=time(NULL))			*/
Xstruct usrtime *usr;	/*	various values from /etc/usrtime				*/
X{
X	return (   check_time (now, usr->enable, 0)  
X			|| check_tty  (tty, usr->ttyline)  
X			|| check_day  (now, usr->days)  
X			|| check_time (now, usr->timerange, 1));
X}
X
X/****************************************************************************
X*	usrtime: ensure usr is on acceptable tty line							*
X****************************************************************************/
X
Xcheck_tty (tty, list)
Xchar	*tty;			/*	user's tty line									*/
Xchar	*list;			/*	list of valid tty lines							*/
X{
X	char	*tt, *ls;
X	int		t;
X
X	if (*list == '\0')
X		return (0);
X	tt = alloca (strlen (tty) + 2);
X	strcpy (tt, tty);
X	strcat (tt, ",");
X	ls = alloca (strlen (list) + 2);
X	strcpy (ls, list);
X	strcat (ls, ",");
X	if ((t = qtscan (ls, tty)) >= 0)
X	{
X		if (ls[t + strlen (tt) - 1] == ',')
X			return (0);
X	}
X	fprintf (stderr, "Login is permitted on %s only.\n", list);
X	return (1);
X}
X
X/****************************************************************************
X*	usrtime: ensure usr is on acceptable week day							*
X****************************************************************************/
X
Xcheck_day (now, list)
Xlong	now;			/*	current time = time(NULL)						*/
Xchar	*list;			/*	list of days (Mon, Tue...)						*/
X{
X	char	weekday[50];
X	
X	if (*list == '\0')
X		return (0);
X	cftime (weekday, "%a", &now);
X	if (tscan (list, weekday) >= 0)
X		return (0);
X	fprintf (stderr, "Login is permitted on %s only.\n", list);
X	return (1);
X}
X
X/****************************************************************************
X*	usrtime: ensure usr is on acceptable time or date range					*
X****************************************************************************/
X
Xcheck_time (now, list, timerange)
Xlong	now;			/*	current time = time(NULL)						*/
Xchar	*list;			/*	hhmm-hhmm or yymmdd-yymmdd list					*/
Xint		timerange;		/*	0 = check yyyymmdd,  1 = check hhmm				*/
X{
X	char	clock[50];
X	char	*begin, *end;
X	char	*ls;
X	long	present, first, last;
X	
X	if (*list < '0'  ||  *list > '9'  ||  strchr (list, '-') == NULL)
X		return (0);
X	ls = end = alloca (strlen (list) + 2);
X	strcpy (end, list);
X	if (timerange)
X		cftime (clock, "%H%M", &now);
X	else
X		cftime (clock, "%Y%m%d", &now);
X	present = atol (clock);
X	while (*end)
X	{	begin = end;
X		if ((end = strchr (begin, '-')) == NULL)
X			break;
X		*end++ = '\0';
X		first = atol (begin);
X		last = atol (end);
X		if (first <= present  &&  present <= last)
X			return (0);
X		if ((end = strchr (end, ',')) == NULL)
X			break;
X		end++;
X	}
X	if (timerange)
X		fprintf (stderr, "Login is permitted between %s hours only.\n", list);
X	else
X		fprintf (stderr, "This login has been disabled.\n");
X	return (1);
X}
X
X#endif
X
X/****************************************************************************
X*	set the users environment with values.									*
X****************************************************************************/
X
Xvoid setenv (this, val)
Xchar *this;
Xchar *val;
X{
X	char	s[MAXPATHLEN+1];
X	char	*ss;
X
X	sprintf (s, "%s=%s", this, val);
X	ss = malloc (strlen(s) + 1);
X	strcpy (ss, s);
X	putenv (ss);
X}
X
X/****************************************************************************
X*	user-provided environment variables must be checked						*
X****************************************************************************/
X
Xvoid check_putenv (val)
Xchar *val;
X{	static count = 0;
X	char	*env;
X	char	**forbid;
X	
X	/*	check forbidden environment variables */
X	for (forbid = forbidden;  *forbid;  forbid++)
X	{	if (strncmp (*forbid, val, strlen (*forbid)) == 0)
X			return;
X	}
X
X	/*	 variables without an '=' sign assume the form of "Lnn=val" */
X	if (strchr (val, '=') == NULL)
X	{	env = malloc (strlen (val)+ 6);
X		sprintf (env, "L%d=%s", count++, val);
X		val = env;
X	}
X	putenv (val);
X}
X
X/****************************************************************************
X*	get the encrypted password from /etc/shadow or /etc/d_passwd			*
X*	allow "null" user for /etc/d_passwd general shell password.				*
X*	Return encrypted password if found, else null.							*
X****************************************************************************/
X
X/*	could have used getspnam(user) except for /etc/d_passwd */
X
Xchar *get_shadow (fname, user, allow)
Xchar *fname;		/*	/etc/shadow or /etc/d_passwd						*/
Xchar *user;			/*	user's login name									*/
Xint	 allow;			/*	0 =must have pwr entry, 1 =success even if no entry	*/
X{	FILE	 *shadow;
X	char	line[SHORTLINE+1];
X	char	*pwd, *colon;
X	static char pass[50];
X
X	*pass = '\0';
X	if (shadow = fopen (fname, "r"))
X	{	while (fgets (line, SHORTLINE, shadow))
X		{	line[SHORTLINE] = '\0';
X			if (pwd = strchr (line, ':'))
X			{	*pwd++ = '\0';
X				if (colon = strchr (pwd, ':'))
X				{	*colon++ = '\0';
X					if (*line)
X					{	if (strcmp (user, line) == 0)
X						{	fclose (shadow);
X							strcpy (pass, pwd);
X							if (allow == 0  &&  colon)
X							{	shadow_lstchg = atol (colon);
X								colon = strchr (colon, ':') + 1;
X								shadow_min = atol (colon);
X								colon = strchr (colon, ':') + 1;
X								shadow_max = atol (colon);
X							}
X							return (pass);
X						}
X					}
X					else if (allow)
X						strcpy (pass, pwd);
X				}
X			}
X		}
X	}
X	fclose (shadow);
X	if (allow  &&  *pass)
X		return (pass);
X	return (NULL);
X}
X
X/****************************************************************************
X*	search for tty type in /etc/ttytype										*
X****************************************************************************/
X
Xchar *search_ttytype (tty)
Xchar *tty;			/*	user's tty line										*/
X{	FILE	*fp;
X	char	line[SHORTLINE+1];
X	static char *tname;
X	char	*dev;
X	
X	if (fp = fopen (TTYTYPE, "r"))
X	{	while (fgets (line, SHORTLINE, fp))
X		{	line[SHORTLINE] = '\0';
X			trim (line);
X			dev = line;
X			while (*dev)				/*	skip tty name 	*/
X			{	if (*dev <= ' ')
X					break;
X				dev++;
X			}
X			while (*dev)				/*	locate device	*/
X			{	if (*dev > ' ')
X					break;
X				*dev++ = '\0';
X			}
X			if (strcmp (dev, tty) == 0)
X			{	fclose (fp);
X				tname = malloc (strlen(line)+1);
X				strcpy (tname, line);
X				return (tname);
X			}
X		}
X	}
X	fclose (fp);
X	return ("UNKNOWN");
X}
X
X
X/****************************************************************************
X*	read /etc/default/login file											*
X****************************************************************************/
X
Xint get_defaults (fname, tz, hz, console, ulim, passreq, altshell, 
X				  path, supath, timeout, cmask, idleweeks)
Xchar *fname;		/*	/etc/default/login									*/
Xchar *tz;			/*	TIMEZONE=xxx	should be same as in /etc/TIMEZONE	*/
Xchar *hz;			/*	HZ=nn												*/
Xchar **console;		/*	CONSOLE=xxx     root must be on this device if given*/
Xlong *ulim;			/*	ULIMIT=xxx		default ulimit for users			*/
Xint	*passreq;		/*	PASSREQ=YES/NO	is password required				*/
Xint *altshell;		/*	ALTSHELL=YES/NO	YES==set shell name in environment	*/
Xchar **path;		/*	PATH=xxx		default user path					*/
Xchar **supath;		/*	SUPATH=xxx		defaut root path					*/
Xlong *timeout;		/*	TIMEOUT=nn		login abort timeout in seconds		*/
Xint  *cmask;		/*	UMASK=ooo		default umask for users				*/
Xint	 *idleweeks;	/*	IDLEWEEKS=nn	idle weeks							*/
X{
X	FILE	*fp;
X	char	line[LONGLINE+1];
X	char	*equal;
X	
X	if ((fp = fopen (fname, "r")) == NULL)
X		return (2);
X	while ((fgets (line, LONGLINE, fp)))
X	{	if (*line == '#')				/*	must be first character */
X			continue;
X		line[LONGLINE] = '\0';
X		trim (line);
X		if (equal = strchr (line, '='))
X		{	*equal++ = '\0';
X			if (strcmp (line, "TIMEZONE") == 0)
X				strcpy (tz, equal);
X			else if (strcmp (line, "HZ") == 0)
X				strcpy (hz, equal);
X			else if (strcmp (line, "CONSOLE") == 0)
X			{	if (*console == NULL)
X				{	*console = malloc (strlen (equal) + 2);
X					strcpy (*console, equal);
X					if (*console[strlen(*console) - 1] != ':')
X						strcat (*console, ":");
X				}
X			}
X			else if (strcmp (line, "ULIMIT") == 0)
X				*ulim = atol (equal);
X			else if (strcmp (line, "PATH") == 0)
X			{	if (*path == NULL)
X				{	*path = malloc (strlen (equal) + 1);
X					strcpy (*path, equal);
X				}
X			}
X			else if (strcmp (line, "SUPATH") == 0)
X			{	if (*supath == NULL)
X				{	*supath = malloc (strlen (equal) + 1);
X					strcpy (*supath, equal);
X				}
X			}
X			else if (strcmp (line, "TIMEOUT") == 0)
X				*timeout = atol (equal);
X			else if (strcmp (line, "UMASK") == 0)
X				*cmask = atoo (equal);
X			else if (strcmp (line, "PASSREQ") == 0)
X			{	if (toupper (*equal) == 'Y')
X					*passreq = 1;
X				else
X					*passreq = 0;
X			}
X			else if (strcmp (line, "ALTSHELL") == 0)
X			{	if (toupper (*equal) == 'Y')
X					*altshell = 1;
X				else
X					*altshell = 0;
X			}
X			else if (strcmp (line, "IDLEWEEKS") == 0)
X				*idleweeks = atoi (equal);
X		}
X	}
X	fclose (fp);
X	return (0);
X}
X
X/****************************************************************************
X*	trim line at end														*
X****************************************************************************/
X
Xvoid trim (s)
Xchar *s;
X{	char	*t;
X
X	t = s + strlen (s);
X	do
X	{	if (*t > ' ')
X			break;
X		*t = '\0';
X	} while (t-- != s);
X}
X
X/****************************************************************************
X*	ascii to octal															*
X****************************************************************************/
X
Xint atoo (s)
Xchar *s;
X{
X	unsigned int v;
X	
X	v = 0;
X	while (*s)
X		v = v * 8 + (*s++ - '0');
X	return (v);
X}
X
X/****************************************************************************
X*	timeout error message if user takes too long to log in.					*
X****************************************************************************/
X
Xvoid user_timeout()
X{
X	fprintf (stderr, "login timed out after %d seconds\n", timeout);
X	exit(0);
X}
X
X
X/****************************************************************************
X*	search for string t in string s.  return -1 if it does not exist		*
X*	otherwise return an integer, the first byte of s that matches t.		*
X****************************************************************************/
X
Xint tscan (s, t)
Xchar 	s[], t[];
X{
X	int	i, j, k;
X	for (i = 0;  s[i] != '\0';  i++)
X	{	for (j = i, k=0;  t[k] != '\0'  &&  s[j] == t[k];  j++, k++)
X			;
X		if (t[k] == '\0')
X			return (i);
X	}
X	return (-1);
X}
X
X/****************************************************************************
X*	search for string t in string s.  return -1 if it does not exist		*
X*	otherwise return an integer, the first byte of s that matches t.		*
X*	Any '?' in s  is a wild card match for t								*
X****************************************************************************/
X
Xint qtscan (s, t)
Xchar 	s[], t[];
X{
X	int	i, j, k;
X	for (i = 0;  s[i] != '\0';  i++)
X	{	for (j = i, k=0;  t[k] != '\0'  &&  ((s[j] == t[k]) || (s[j] == '?')) ;  j++, k++)
X			;
X		if (t[k] == '\0')
X			return (i);
X	}
X	return (-1);
X}
X
END_OF_FILE
if test 30942 -ne `wc -c <'login.c'`; then
    echo shar: \"'login.c'\" unpacked with wrong size!
fi
# end of 'login.c'
fi
echo shar: End of shell archive.
exit 0

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.