jfh@rpp386.Dallas.TX.US (John F. Haugh II) (06/20/89)
X-Archive-Name: shadow2/part0 The next three articles, hopefully, should constitute the beta release of the second version of my shadow login implementation. The previous version is executing on approximately 200 machines of a dozen or more configurations. All known bugs have been corrected and a number of features have been added. Please read config.h and README for details. Minor anomolies exist in the handling of subsystem logins [ I don't know exactly how they should work for the different systems ]. Also, Altos and UNIX-PCs have some serious problems. Down with bogus UNIX implementations! Major changes include the addition of some BSD code. This still needs much work. The handling of dialup password, console restriction, and general password failure is now more standardized. I would like someone to add code to handle remote logins. This code is presently in use on this system. Most all of the options have been turned on. Problems may arise if you use some combination of flags I haven't considered. Please tell me your configuration when reporting bugs. -- John F. Haugh II +-Button of the Week Club:------------- VoiceNet: (512) 832-8832 Data: -8835 | "AIX is a three letter word, InterNet: jfh@rpp386.Cactus.Org | and it's BLUE." UucpNet : <backbone>!bigtex!rpp386!jfh +--------------------------------------
jfh@rpp386.Dallas.TX.US (John F. Haugh II) (06/20/89)
X-Archive-Name: shadow2/part1 Part 1 of second USENET release -- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # log.c # mail.c # shadow.h # sulog.c # Makefile # entry.c # obscure.c # setup.c # sub.c # config.h # pmain.c # sulogin.c # dialup.h # ttytype.c # This archive created: Tue Jun 20 01:28:03 1989 # By: John F. Haugh II (River Parishes Programming, Plano TX) export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \SHAR_EOF > 'README' This is the explanatory document for John F. Haugh II's login replacement. This software is copyright 1989, John F. Haugh II. All rights reserved. Use, duplication and disclosure is permitted according to the guidelines listed below. At some point in the future this licence will be modified to conform to the GNU General Public License. This software is being provided as a freely redistributable login clone. You may distribute this software provided you do not charge for other than transmission costs. You are free to transfer this software provided you do not restrict the rights of the recipients to further transfer this software. This software is being distributed AS-IS. The author disclaims all liability for any consequences of use. The user is solely responsible for the maintenance of this software package. The author is under no obligation to provide modifications or improvements. Begin by reading and editing the config.h file. All options are selected by using #define's. A brief description for each available option appears below. You may want to print this file out as it is LONG and you will need to refer to it while editting config.h. You will also have to edit the Makefile. The possible differences are documented there. Pay close attention to the install: rule. DO NOT MAIL ME DIFFERENCES FOR VARIOUS INSTALLATION PROBLEMS. If you must share your experiences, do so on the net. Login now runs on about 15 different varieties of UNIX that I have been made aware of. Note that there are MANY options. As distributed most options are turned on, which produces a really nice package. This is the system as used on the authors machine. [ The one exception is NOBLANK, that is turned off because of anonymous UUCP logins. ] Dialup Password Files - This option permits individual ports to have an additional password prompted for on a by-shell basis. /etc/dialups contains a list of dialup ports, d_passwd contains the password for each shell requiring a dialup password. Select this option by defining the DIALUP macro. Shadow [ unreadable ] Password Files - This option utilizes an alternate, non-readable file to contain the actual encrypted passwords. This is presumed to increase system security by increasing the difficulty with which system crackers obtain encrypted passwords. Select this option by defining the SHADOWPWD macro. Double Length Passwords - This option extends the maximum length of a user password to 16 characters from eight. Select this option by defining the DOUBLESIZE macro. Credit for this option is due Jonathan Bayer. Obscure Password Testing - This option includes code to test user passwords for complexity. The programmer is encouraged to edit the file obscure.c to add additional methods for detecting simplistic passwords. Select this option by defining the OBSCURE macro. Additionally, the PASSLENGTH macro must be defined to control the minimum length for a legal password. Mandatory Password Prompting - This option requires all passwords, including null ones, to be prompted for. Traditionally an account with a password field of '::' does not require prompting for. This option modifies this behavior to require even null passwords be prompted for. Select this option by defining the NOBLANK macro. Password Aging Defaults - You may select the default number of days during which a password is valid. The pwconv command adds aging information to accounts which do not include it already. The MINDAYS macro must be defined to be the minimum number of days which must pass before a password may be changed. The MAXDAYS macro must be defined to be the maximum number of days which a password will remain valid during. HZ Environmental Variable - This option pre-defines the HZ environmental variable. Certain systems require this variable be defined for system time reporting functions to work properly. Select this option by defining the HZ macro to have the desired environmental variable value. TZ Environmental Variable - This option pre-defines the TZ environmental variable. This provides a default timezone variable for use by various utilities. Select this option by defining the TZ macro to have the desired environmental variable value, or the name of the file containing the desired value. Password Aging - This option includes code to perform password aging. Password aging is presumed to increase system security by forcing users to change passwords on a regular basis. The resolution on password age is in weeks for non-shadow password systems and in days otherwise. Select this option by defining the AGING macro. Mailbox Checking - This option includes code to check the status of the user's mailbox. One of three messages are produced depending on the status of the user's mailbox. Select this option by defining the MAILCHECK macro. Console Restricted Root Logins - This option restricts the port which root may legally login on. This option presumably increases system security by preventing outside attacks against the root account. Select this option by defining the CONSOLE macro to have the desired port name. If this file is a regular file, it is considered to contain a list of legal port names, one per line. Note that the port names DO NOT begin with "/dev/" and that a file name would have to be fully qualified. See config.h for a pair of examples. Restricted User Logins - This option permits you to specify a file which disables user logins. This options permits you to keep normal users off of the system while performing maintenance functions. Select this option by defining NOLOGINS to be the name of the file to use. Restricted Use Accounts - This option permits certain accounts to be used for identification purposes only. This options associates login ID's with UID's, such as for disk space accounting or anonymous FTP accounts. Passwords for these accounts may only be changed by root. Select this option by defining NOUSE to be the string to include in the password file in place of the user's shell. Message of the Day Printing - This option causes the message of the day to be printed at login time. Select this option by defining the MOTD macro. If you wish this feature to be overriden on a per-user basis, define the macro HUSHLOGIN and users may then turn off the /etc/motd message by creating a file '.hushlogin' in their home directories. Last Login Time Logging - This option causes a record to be made of successful logins in /usr/adm/lastlog. The format of the structure is defined in lastlog.h. Select this option by defining the LASTLOG macro. You will need to determine if you system already has a lastlog.h file and use that file if present. Failed Login Logging - This option causes a record to be kept of the most recent login failure by date and port. A cummulative count of failures is maintained and compared against an allowable limit. Select this option by defining the FAILLOG macro. See the file faillog.h for more details. Terminal Permissions - This option allows the terminal modes to be set at login time. This is particularly useful to disable messages on user's terminals. Select this option by defining the TTYPERM macro as having the desired mode. Terminal Type Setup - This option allows the terminal type to be set at login time. The environmental variable TERM will be set from the specified terminal to port mapping file. Select this option by defining the TTYTYPE macro as having the value of the name of the type to port mapping file. Credit for this option is due Chip Rosenthal. File Size Setting - This option includes code to set the user's ulimit at login time. Additional code to set the umask and nice value is also included. Select this option by defining the QUOTAS macro. Switch-User Logging - This option causes su(1) to log attempts to switch users. Su(1) will log all attempt, giving the old and new user ID's, tty port, and time. It also indicates if the attempt was successful. Select this option by defining the SULOG macro to have the value of the name of the file you want attempts logged to. Configurable Editing Keys - This options allows the erase and kill characters to be selected. A default value is provided. By default ERASE will be ^H and KILL will be ^U. Select this option by defining the ERASECHAR macro to be the desired erase character and the KILLCHAR macro to be the desired KILL character. Default ulimit and umask Values - This option allows you to select the default values for ulimit and umask, allowing you to avoid regenerating your system kernel. These values may be overriden with appropriate entries in the GECOS field. Select the default ulimit by defining the ULIMIT macro, and the default umask by defining the UMASK macro. Warning: These values will not apply to processes executed by /etc/cron or any of their children. BSD Notes: Steve Simmons scs@iti.org The full port of the shadow package to BSD is not complete; but some of the issues have been worked out. These notes describe the current state of things: In order to make use of password aging under BSD, minor changes to /usr/include/pwd.h and getpwent() are needed. These changes are to keep the password age from messing up the encrypted password when not using shadow passwords, and involve placing a new field in the password data structure. To use this, you should apply the following two patches: pwd.h.patch getpwent.c.patch to the BSD /usr/include/pwd.h and /usr/src/lib/libc/gen/getpwent.c, respectively. After applying the patches, rebuild your standard C library with the new getpwent. Programs which use the old getpwent will fail on password checking if they do a strcmp rather than a strncmp. These changes are based on BSD4.3, not Tahoe ToDo BSD: I'm working on this in my copious spare time (hah!); any help would be appreciated. If you decide to help, do these independantly rather than rework BSD code! Keep it redistributable! No dbm functions have been put in place. Dbm functionality is needed for both /etc/password and /etc/shadow management. The BSD GECOS field gets used for lots more stuff than the USG. At a minimum this functionality should be duplicated under BSD; better is to put it into USG as well; still better would be to make the chfn command for both systems; best would be site-configurable data to be put into GECOS/chfn. SHAR_EOF fi if test -f 'log.c' then echo shar: "will not over-write existing file 'log.c'" else cat << \SHAR_EOF > 'log.c' #include <sys/types.h> #include <utmp.h> #include <pwd.h> #include <fcntl.h> #include <time.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifndef lint static char _sccsid[] = "@(#)log.c 2.1 01:23:21 6/20/89"; #endif #ifdef LASTLOG #include "lastlog.h" extern struct utmp utent; extern struct passwd pwent; extern struct lastlog lastlog; extern char **environ; long lseek (); time_t time (); void log () { int fd; off_t offset; struct lastlog newlog; if ((fd = open ("/usr/adm/lastlog", O_RDWR)) == -1) return; offset = pwent.pw_uid * sizeof lastlog; if (lseek (fd, offset, 0) != offset) { (void) close (fd); return; } if (read (fd, (char *) &lastlog, sizeof lastlog) != sizeof lastlog) #ifndef BSD memset ((char *) &lastlog, sizeof lastlog, 0); #else bzero ((char *) &lastlog, sizeof lastlog); #endif newlog = lastlog; (void) time (&newlog.ll_time); (void) strncpy (newlog.ll_line, utent.ut_line, sizeof newlog.ll_line); (void) lseek (fd, offset, 0); (void) write (fd, (char *) &newlog, sizeof newlog); (void) close (fd); } #endif SHAR_EOF fi if test -f 'mail.c' then echo shar: "will not over-write existing file 'mail.c'" else cat << \SHAR_EOF > 'mail.c' #include <sys/types.h> #include <sys/stat.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifndef lint static char _sccsid[] = "@(#)mail.c 2.1 01:23:26 6/20/89"; #endif extern char mail[]; #ifdef HUSHLOGIN extern int hushed; #endif #ifdef MAILCHECK void mailcheck () { struct stat statbuf; char *mailbox; #ifdef HUSHLOGIN if (hushed) return; #endif if (mailbox = strchr (mail, '=')) mailbox++; else return; if (stat (mailbox, &statbuf) == -1 || statbuf.st_size == 0) puts ("No mail."); else if (statbuf.st_atime > statbuf.st_mtime) puts ("You have mail."); else puts ("You have new mail."); } #endif SHAR_EOF fi if test -f 'shadow.h' then echo shar: "will not over-write existing file 'shadow.h'" else cat << \SHAR_EOF > 'shadow.h' /* * This information is not derived from AT&T licensed sources. Posted * to the USENET 11/88. * * @(#)shadow.h 2.1 01:23:51 6/20/89 */ /* * Shadow password security file structure. */ struct spwd { char *sp_namp; /* login name */ char *sp_pwdp; /* encrypted password */ long sp_lstchg; /* date of last change */ long sp_max; /* maximum number of days between changes */ long sp_min; /* minimum number of days between changes */ }; /* * Shadow password security file functions. */ struct spwd *getspent (); struct spwd *getspnam (); void setspent (); void endspent (); struct spwd *fgetspent (); int putspent (); #define SHADOW "/etc/shadow" SHAR_EOF fi if test -f 'sulog.c' then echo shar: "will not over-write existing file 'sulog.c'" else cat << \SHAR_EOF > 'sulog.c' #include <sys/types.h> #include <stdio.h> #include <time.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" extern char name[]; extern char oldname[]; time_t time (); void sulog (success) int success; { #ifdef SULOG char *tty; char *cp; char *ttyname (); time_t clock; struct tm *tm; struct tm *localtime (); FILE *fp; if ((fp = fopen (SULOG, "a+")) == (FILE *) 0) return; /* can't open or create logfile */ (void) time (&clock); tm = localtime (&clock); if (isatty (0) && (cp = ttyname (0))) { if (tty = strrchr (cp, '/')) tty++; else tty = cp; } else tty = "???"; (void) fprintf (fp, "SU %.02d/%0.2d %.02d:%.02d %c %.6s %s-%s\n", tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, success ? '+':'-', tty, oldname, name); fflush (fp); fclose (fp); #endif } SHAR_EOF fi if test -f 'Makefile' then echo shar: "will not over-write existing file 'Makefile'" else cat << \SHAR_EOF > 'Makefile' # # %W% %U% - System V shadow password system # # %W% %U% %G% # SHELL = /bin/sh # Define the directory login is copied to. BE VERY CAREFUL!!! # LOGINDIR = /bin LOGINDIR = /etc # Pick your favorite C compiler and tags command CC = cc TAGS = ctags # OS. Currently only BSD and USG are defined. If you don't use BSD, # USG (System V) is assumed. # OS = -DBSD # Do you have to do ranlib? Sorry to hear that ... RANLIB = ranlib # RANLIB = echo # Flags for SCO Xenix/386 CFLAGS = -O -M3 -g $(PWDEF) $(AL64DEF) $(OS) LIBS = -lcrypt LDFLAGS = -M3 -g LTFLAGS = # This should be Slibsec.a for small model, or Llibsec.a for # large model or whatever. MUST AGREE WITH CFLAGS!!! LIBSEC = Slibsec.a # Flags for normal machines # CFLAGS = -O -g $(PWDEF) $(AL64DEF) $(OS) # LIBS = # LDFLAGS = -g # LIBSEC = libsec.a LOBJS = lmain.o login.o env.o password.o entry.o valid.o setup.o shell.o age.o \ pwent.o utmp.o sub.o mail.o motd.o log.o shadow.o dialup.o dialchk.o \ ttytype.o failure.o LSRCS = lmain.c login.c env.c password.c entry.c valid.c setup.c shell.c age.c \ pwent.c utmp.c sub.c mail.c motd.c log.c shadow.c dialup.c dialchk.c \ ttytype.c failure.c SOBJS = smain.o env.o password.o entry.o valid.o susetup.o sushell.o \ pwent.o susub.o mail.o motd.o sulog.o shadow.o age.o SSRCS = smain.c env.c password.c entry.c valid.c setup.c shell.c \ pwent.c sub.c mail.c motd.c sulog.c shadow.c age.c POBJS = pmain.o password.o entry.o valid.o pwage.o pwent.o obscure.o shadow.o PSRCS = pmain.c password.c entry.c valid.c age.c pwent.c obscure.c shadow.c PWOBJS = pwconv.o pwent.o shadow.o pwage.o PWSRCS = pwconv.c pwent.c shadow.c age.c PWUNOBJS = pwunconv.o pwent.o shadow.o pwage.o PWUNSRCS = pwunconv.c pwent.c shadow.c age.c SULOGOBJS = sulogin.o entry.o env.o password.o pwage.o pwent.o setup.o \ shadow.o shell.o valid.o SULOGSRCS = sulogin.c entry.c env.c password.c age.c pwent.c setup.c \ shadow.c shell.c valid.c ALLSRCS = age.c dialchk.c dialup.c entry.c env.c lmain.c log.c login.c mail.c \ motd.c obscure.c password.c pmain.c pwconv.c pwent.c pwunconv.c \ setup.c shadow.c shell.c smain.c sub.c sulog.c sulogin.c ttytype.c \ utmp.c valid.c FILES1 = README log.c mail.c shadow.h sulog.c Makefile entry.c obscure.c \ setup.c sub.c config.h pmain.c sulogin.c dialup.h ttytype.c FILES2 = lastlog.h login.c motd.c password.c shell.c utmp.c age.c env.c \ pwent.c shadow.c valid.c lmain.c smain.c pwconv.c dialup.c dialchk.c \ pwunconv.c failure.c faillog.h faillog.c DOCS = login.1 passwd.1 passwd.4 shadow.3 shadow.4 su.1 sulogin.8 pwconv.8 \ pwunconv.8 faillog.8 faillog.4 all: su login pwconv pwunconv passwd sulogin faillog libsec: shadow.o ar rv $(LIBSEC) shadow.o $(RANLIB) $(LIBSEC) install: all cp login $(LOGINDIR)/login cp pwconv pwunconv sulogin /etc cp su passwd faillog /bin chown root $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \ /bin/su /bin/passwd chgrp root $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \ /bin/su /bin/passwd chown bin /bin/faillog chgrp bin /bin/faillog chmod 700 /etc/pwconv /etc/pwunconv /etc/sulogin chmod 4711 $(LOGINDIR)/login /bin/su /bin/passwd chmod 711 /bin/faillog lint: su.L login.L pwconv.L pwunconv.L passwd.L sulogin.L faillog.L tags: $(ALLSRCS) $(TAGS) $(ALLSRCS) login: $(LOBJS) $(CC) -o login $(LDFLAGS) $(LOBJS) $(LIBS) login.L: $(LSRCS) lint $(LSRCS) > login.L su: $(SOBJS) $(CC) -o su $(LDFLAGS) $(SOBJS) $(LIBS) su.L: $(SSRCS) lint -DSU $(SSRCS) > su.L passwd: $(POBJS) $(CC) -o passwd $(LDFLAGS) $(POBJS) $(LIBS) passwd.L: $(PSRCS) lint -DPASSWD $(PSRCS) > passwd.L pwconv: $(PWOBJS) $(CC) -o pwconv $(LDFLAGS) $(PWOBJS) $(LIBS) pwconv.L: $(PWSRCS) lint -DPASSWD $(PWSRCS) > pwconv.L pwunconv: $(PWUNOBJS) $(CC) -o pwunconv $(LDFLAGS) $(PWUNOBJS) $(LIBS) pwunconv.L: $(PWUNSRCS) lint -DPASSWD $(PWUNSRCS) > pwunconv.L sulogin: $(SULOGOBJS) $(CC) -o sulogin $(LDFLAGS) $(SULOGOBJS) $(LIBS) sulogin.L: $(SULOGSRCS) lint $(SULOGSRCS) > sulogin.L faillog: faillog.o $(CC) -o faillog $(LDFLAGS) faillog.o $(LIBS) faillog.L: faillog.c faillog.h config.h lint faillog.c > faillog.L sushell.o: config.h shell.c $(CC) -c $(CFLAGS) -DSU shell.c mv shell.o sushell.o susub.o: config.h sub.c $(CC) -c $(CFLAGS) -DSU sub.c mv sub.o susub.o sulog.o: config.h susetup.o: config.h setup.c $(CC) -c $(CFLAGS) -DSU setup.c mv setup.o susetup.o pmain.o: config.h lastlog.h shadow.h pwage.o: age.c config.h cp age.c pwage.c $(CC) -c $(CFLAGS) -DPASSWD pwage.c rm pwage.c lmain.o: config.h lastlog.h smain.o: config.h lastlog.h setup.o: config.h utmp.o: config.h mail.o: config.h motd.o: config.h age.o: config.h log.o: config.h lastlog.h shell.o: config.h entry.o: config.h shadow.h shadow.o: shadow.h dialup.o: dialup.h dialchk.o: dialup.h config.h valid.o: config.h failure.o: faillog.h config.h faillog.o: faillog.h config.h pwent.o: config.h clean: -rm -f *.o a.out core npasswd nshadow clobber: clean -rm -f su login passwd pwconv pwunconv sulogin faillog *.L shar: login.sh.1 login.sh.2 login.sh.3 login.sh.1: $(FILES1) shar $(FILES1) > login.sh.1 login.sh.2: $(FILES2) shar $(FILES2) > login.sh.2 login.sh.3: $(DOCS) shar $(DOCS) > login.sh.3 SHAR_EOF fi if test -f 'entry.c' then echo shar: "will not over-write existing file 'entry.c'" else cat << \SHAR_EOF > 'entry.c' #include <stdio.h> #include <pwd.h> #ifndef BSD #include <string.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifdef SHADOWPWD #include "shadow.h" #endif #ifndef lint static char _sccsid[] = "@(#)entry.c 2.1 01:23:09 6/20/89"; #endif struct passwd *fgetpwent (); char *malloc (); char *strdup (s) char *s; { char *cp; if (s == (char *) 0) return ((char *) 0); if (! (cp = malloc ((unsigned) strlen (s) + 1))) return ((char *) 0); return (strcpy (cp, s)); } void entry (name, pwent) char *name; struct passwd *pwent; { FILE *pwd; struct passwd *passwd; #ifdef SHADOWPWD struct spwd *spwd; char *l64a (); #endif char *cp; if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0) { pwent->pw_passwd = (char *) 0; return; } while (passwd = fgetpwent (pwd)) { if (strcmp (name, passwd->pw_name) == 0) break; } fclose (pwd); if (passwd == (struct passwd *) 0) { pwent->pw_name = (char *) 0; pwent->pw_passwd = (char *) 0; } else { pwent->pw_name = strdup (passwd->pw_name); pwent->pw_uid = passwd->pw_uid; pwent->pw_gid = passwd->pw_gid; pwent->pw_comment = (char *) 0; pwent->pw_gecos = strdup (passwd->pw_gecos); pwent->pw_dir = strdup (passwd->pw_dir); pwent->pw_shell = strdup (passwd->pw_shell); #ifdef SHADOWPWD setspent (); if (spwd = getspnam (name)) { pwent->pw_passwd = strdup (spwd->sp_pwdp); if (spwd->sp_lstchg != 0) { pwent->pw_age = (char *) 0; } else { pwent->pw_age = malloc (5); pwent->pw_age[0] = i64c (spwd->sp_max / 7); pwent->pw_age[1] = i64c (spwd->sp_min / 7); cp = l64a (spwd->sp_lstchg / 7); pwent->pw_age[2] = cp[0]; pwent->pw_age[3] = cp[1]; pwent->pw_age[4] = '\0'; } endspent (); return; } endspent (); passwd->pw_age = pwent->pw_age = (char *) 0; #endif if (passwd->pw_passwd) pwent->pw_passwd = strdup (passwd->pw_passwd); else pwent->pw_passwd = (char *) 0; if (passwd->pw_age) { pwent->pw_age = malloc (5); /* longest legal time */ (void) strncpy (pwent->pw_age, passwd->pw_age, 5); } else pwent->pw_age = (char *) 0; } } SHAR_EOF fi if test -f 'obscure.c' then echo shar: "will not over-write existing file 'obscure.c'" else cat << \SHAR_EOF > 'obscure.c' #include <ctype.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifndef lint static char _sccsid[] = "@(#)obscure.c 2.1 01:23:29 6/20/89"; #endif /* * Obscure - see if password is obscure enough. * * The programmer is encouraged to add as much complexity to this * routine as desired. Included are some of my favorite ways to * check passwords. */ extern char pass[]; /* the new password */ extern char orig[]; /* the original password */ #ifdef OBSCURE char mono[32]; /* a monocase version of pass */ #endif int obscure () { #ifdef OBSCURE int i; #endif if (orig[0] == '\0') return (1); if (strlen (pass) < PASSLENGTH) { /* too short */ printf ("Too short. "); return (0); } #ifdef OBSCURE for (i = 0;pass[i];i++) mono[i] = tolower (pass[i]); if (strcmp (pass, orig) == 0) /* the same */ return (0); if (palindrome ()) /* a palindrome */ return (0); if (caseshift ()) /* upper/lower case changes only */ return (0); if (similiar ()) /* jumbled version of original */ return (0); #endif return (1); } #ifdef OBSCURE /* * can't be a palindrome - like `R A D A R' or `M A D A M' */ int palindrome () { int i, j; i = strlen (pass); for (j = 0;j < i;j++) if (pass[i - j - 1] != pass[j]) return (0); printf ("No palindromes. "); return (1); } /* * may not be a shifted version of original */ int caseshift () { int i; for (i = 0;pass[i] && orig[i];i++) { if (tolower (pass[i]) == tolower (orig[i])) continue; else return (0); } printf ("May not be case-shifted. "); return (1); } /* * more than half of the characters are different ones. */ int similiar () { int i, j; char *strchr (); for (i = j = 0;pass[i] && orig[i];i++) if (strchr (mono, tolower (orig[i]))) j++; if (i - j > 2) return (0); printf ("Too similiar. "); return (1); } #endif SHAR_EOF fi if test -f 'setup.c' then echo shar: "will not over-write existing file 'setup.c'" else cat << \SHAR_EOF > 'setup.c' #include <sys/types.h> #include <pwd.h> #include <utmp.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifndef lint static char _sccsid[] = "@(#)setup.c 2.1 01:23:45 6/20/89"; #endif extern char home[]; extern char prog[]; extern char name[]; extern char mail[]; #ifndef PATH #define PATH ":/bin:/usr/bin" #endif #ifndef SUPATH #define SUPATH ":/bin:/usr/bin:/etc" #endif #ifndef MAILDIR #define MAILDIR "/usr/spool/mail" #endif #ifndef TTYPERM #define TTYPERM 0622 #endif #ifndef SU extern struct utmp utent; #endif #ifdef QUOTAS long strtol (); #ifdef ULIMIT long ulimit (); #endif #endif void addenv (); void setup (info) struct passwd *info; { extern int errno; char logname[30]; #ifndef SU char tty[30]; #endif char *cp; int i; long l; #ifndef SU (void) strcat (strcpy (tty, "/dev/"), utent.ut_line); if (chown (tty, info->pw_uid, info->pw_gid) || chmod (tty, TTYPERM)) perror (tty); #endif if (chdir (info->pw_dir) == -1) { (void) printf ("Unable to change directory to \"%s\"\n", info->pw_dir); exit (errno); } #ifdef QUOTAS for (cp = info->pw_gecos;cp != (char *) 0;cp = strchr (cp, ',')) { if (*cp == ',') cp++; if (strncmp (cp, "pri=", 4) == 0) { i = atoi (cp + 4); if (i >= -20 && i <= 20) (void) nice (i); continue; } #ifdef ULIMIT if (strncmp (cp, "ulimit=", 7) == 0) { l = strtol (cp + 7, (char **) 0, 10); (void) ulimit (2, l); continue; } #endif if (strncmp (cp, "umask=", 6) == 0) { i = strtol (cp + 6, (char **) 0, 8) & 0777; (void) umask (i); continue; } } #endif if (setgid (info->pw_gid) == -1) { puts ("Bad group id"); exit (errno); } #ifndef BSD if (setuid (info->pw_uid)) #else if (setreuid (info->pw_uid, info->pw_uid)) #endif { puts ("Bad user id"); exit (errno); } (void) strcat (strcpy (home, "HOME="), info->pw_dir); addenv (home); if (info->pw_shell == (char *) 0) info->pw_shell = "/bin/sh"; (void) strcat (strcpy (prog, "SHELL="), info->pw_shell); addenv (prog); if (info->pw_uid == 0) addenv (SUPATH); else addenv (PATH); (void) strcat (strcpy (logname, "LOGNAME="), name); addenv (logname); (void) strcat (strcat (strcpy (mail, "MAIL="), MAILDIR), name); addenv (mail); } SHAR_EOF fi if test -f 'sub.c' then echo shar: "will not over-write existing file 'sub.c'" else cat << \SHAR_EOF > 'sub.c' #include <sys/types.h> #include <pwd.h> #include <utmp.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #ifndef lint static char _sccsid[] = "@(#)sub.c 2.1 01:23:58 6/20/89"; #endif extern struct passwd pwent; #ifndef SU extern struct utmp utent; #endif void setutmp (); /* * I have heard of two different types of behavior with subsystem roots. * One has you execute login no matter what. The other has you execute * the command [ if one exists ] after the '*' in the shell name. The * macro SUBLOGIN says to execute /bin/login [ followed by /etc/login ] * regardless. Otherwise, pwent.pw_shell is fixed up and that command * is executed [ by returning to the caller ]. I prefer the latter since * it doesn't require having a "login" on the new root filesystem. */ void subsystem () { char *strdup (); if (pwent.pw_dir[0] != '/') exit (1); if (chdir (pwent.pw_dir) || chroot (pwent.pw_dir)) { printf ("Can't change to \"%s\"\n", pwent.pw_dir); exit (1); } #ifndef SU (void) strcpy (utent.ut_line, "<!sublogin>"); setutmp (); #endif #ifdef SUBLOGIN execl ("/bin/login", "login", name, (char *) 0); execl ("/etc/login", "login", name, (char *) 0); puts ("No /bin/login or /etc/login on root"); exit (1); #else if (! pwent.pw_shell || strlen (pwent.pw_shell) == 1) pwent.pw_shell = "/bin/sh"; /* default shell */ else pwent.pw_shell++; /* skip over '*' */ #endif } SHAR_EOF fi if test -f 'config.h' then echo shar: "will not over-write existing file 'config.h'" else cat << \SHAR_EOF > 'config.h' /* * Configuration file for login. * * @(#)config.h 2.1 01:23:03 6/20/89 */ /* * Define DIALUP to use dialup password files */ #define DIALUP /* * Define SHADOWPWD to use shadow [ unreadable ] password file */ #define SHADOWPWD /* * Define DOUBLESIZE to use 16 character passwords */ #define DOUBLESIZE /* * Define OBSCURE to include hard password testing code. */ #define OBSCURE /* * Define PASSLENGTH to be shortest legal password */ #define PASSLENGTH 5 /* * Define NOBLANK if you want all passwords prompted for, including * empty ones. #undef NOBLANK /* * Define MAXDAYS to be the default maximum number of days a password * is valid for when converting to shadow passwords. Define MINDAYS * to be the minimum number of days before a password may be changed. * See pwconv.c for more details. */ #define MAXDAYS 10000 #define MINDAYS 0 /* * Define NDEBUG for production versions */ #define NDEBUG /* * Define HZ if login must set HZ value */ #define HZ "HZ=50" /* * Define TZ if login must set timezone * * The first example sets the variable directly. The * second example names a file which is read to determine * the proper value. The file consists of a single line * of the form 'TZ=zone-name' */ /* #define TZ "TZ=CST6CDT" */ #define TZ "/etc/tzname" /* * Define the default PATH and SUPATH here. PATH is for non-privileged * users, SUPATH is for root. The first pair are for real trusting * systems, the second pair are for the paranoid ... */ /* #define PATH "PATH=:/bin:/usr/bin" */ /* #define SUPATH "PATH=:/bin:/usr/bin:/etc" */ #define PATH "PATH=/bin:/usr/bin" #define SUPATH "PATH=/bin:/usr/bin:/etc" /* * Define the mailbox directory */ #define MAILDIR "/usr/spool/mail/" /* * Define AGING if you want the password aging checks made. */ #define AGING /* * Define MAILCHECK if you want the mailbox checked for new mail * * One of two messages are printed - `You have new mail.' or * `You have mail.'. */ #define MAILCHECK /* * Define CONSOLE if you want ROOT restricted to a particular terminal. * * Use the name of the tty line if you only want a single line, or use * the name of the file containing the permissible ports if you wish to * allow root logins on more than one port. */ /* #define CONSOLE "console" /* root on /dev/console only */ #define CONSOLE "/etc/consoles" /* check /etc/consoles for a list */ /* * Define NOLOGINS if you want to be able to deny non-root users logins. * Logins will not be permitted if this file exists. */ #define NOLOGINS "/etc/nologin" /* * Define NOUSE if you want to be able to declare accounts which can't * be logged into. */ #define NOUSE "NOUSE" /* * Define MOTD if you want the message of the day (/etc/motd) printed * at login time. */ #define MOTD /* * Define HUSHLOGIN if you want the code added to avoid printing the * motd if a file $HOME/.hushlogin exists. This obviously only matters * if any of MOTD, MAILCHECK or LASTLOG are #define'd. */ #define HUSHLOGIN /* * Define LASTLOG if you want a record made of logins in /usr/adm/lastlog. */ #define LASTLOG /* * Define FAILLOG if you want a record make of failed logins in * /usr/adm/faillog. See faillog.h for more details. See fail(1L) * for even still more details ... */ #define FAILLOG /* * Define TTYPERM to be the initial terminal permissions. Defining * as 0600 will not allow messages, 0622 will. */ #define TTYPERM 0600 /* * Define TTYTYPE to the be name of the port to terminal type * mapping file. This is used to set the environmental variable * "TERM" to the correct terminal type. */ #define TTYTYPE "/etc/ttytype" /* * Define QUOTAS if you want the code added in setup.c to support * file ulimit and nice [ and umask as well ] setting from the password * file. */ #define QUOTAS /* * Define file name for sulog. If SULOG is not defined, there will be * no logging. This is NOT a good idea ... We also define other file * names. */ #define SULOG "/usr/adm/sulog" #define PWDFILE "/etc/passwd" #define OPWDFILE "/etc/-passwd" #define NPWDFILE "/etc/npasswd" #define OSHADOW "/etc/-shadow" #define NSHADOW "/etc/nshadow" /* * Define PWDLOCK to be a locking semaphore for updating the password * file. */ #define PWDLOCK "/etc/.pwdlock" /* * Wierd stuff follows ... * * The following macros exist solely to override stuff ... * You will probably want to change their values to suit your * fancy. */ #define ERASECHAR '\b' #define KILLCHAR '\025' #define UMASK 022 #define ULIMIT (1L<<20) /* Define if your UNIX supports ulimit() */ #define FGETPWENT /* Define if library does not include FGETPWENT */ #define NEED_AL64 /* Define if library does not include a64l() */ SHAR_EOF fi if test -f 'pmain.c' then echo shar: "will not over-write existing file 'pmain.c'" else cat << \SHAR_EOF > 'pmain.c' #include <sys/types.h> #include <stdio.h> #include <pwd.h> #include <fcntl.h> #include <signal.h> #include <errno.h> #ifndef BSD #include <termio.h> #include <string.h> #include <memory.h> #else #include <sgtty.h> #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #include "lastlog.h" #include "shadow.h" #ifndef PASSLENGTH #define PASSLENGTH 5 #endif #ifndef lint static char _sccsid[] = "@(#)pmain.c 2.1 01:23:35 6/20/89"; #endif char name[BUFSIZ]; char orig[BUFSIZ]; char pass[BUFSIZ]; char pass2[BUFSIZ]; struct passwd pwent; #ifndef RETRIES #define RETRIES 3 #endif char *l64a (); char *crypt (); extern int errno; long a64l (); void entry (); time_t time (); int main (argc, argv) int argc; char **argv; { void die (); char *cp; char *getlogin (); int amroot; int lockfd = -1; #ifdef OBSCURE int force = 0; #endif int retries; #ifdef AGING long week; long lastweek; #endif long salttime; struct passwd *pw; struct passwd *getpwuid (); struct passwd *sgetpwent (); FILE *npwd; #ifdef SHADOWPWD struct spwd *spwd; struct spwd tspwd; #else FILE *pwd; char buf[BUFSIZ]; #endif char tmp[BUFSIZ]; argc--; argv++; /* shift ... */ if (! isatty (0) || ! isatty (1)) exit (1); die (0); /* save tty modes */ signal (SIGHUP, die); /* exit if SIGHUP */ signal (SIGINT, die); /* exit if SIGINT */ signal (SIGQUIT, die); /* exit if SIGQUIT */ signal (SIGTERM, die); /* exit if SIGTERM */ if (! (pw = getpwuid (getuid ()))) goto failure; /* can't get my name ... */ #ifdef OBSCURE if (argc > 0 && strcmp (argv[0], "-f") == 0) { force = 1; argc--; argv++; /* shift ... */ } #endif if (argc > 0) (void) strcpy (name, argv[0]); else if (cp = getlogin ()) /* need user name */ (void) strcpy (name, cp); else /* can't find user name! */ goto failure; printf ("Changing password for %s\n", name); amroot = getuid () == 0; /* currently am super user */ #ifdef OBSCURE if (! amroot) force = 0; #endif if (! amroot && strcmp (name, pw->pw_name) != 0) goto failure; entry (name, &pwent); /* get password file entry */ if (! pwent.pw_name) /* no entry for user??? */ goto failure; if (! amroot) { if (! password ("Old Password:", orig)) exit (1); if (! valid (orig, &pwent)) { puts ("Sorry."); exit (1); } } #ifdef AGING if (! amroot && pwent.pw_age) { /* check out the age */ #ifdef SHADOWPWD (void) time (&week); week /= (24L * 60L * 60L); /* days since epoch */ if (spwd = getspnam (name)) { /* use entries in shadow */ if (spwd->sp_min > spwd->sp_max) { puts ("You may not change this password"); exit (1); } if (spwd->sp_lstchg + spwd->sp_min > week) { printf ("Sorry, less than %d days since the last change\n", spwd->sp_min); exit (1); } } else { #endif /* SHADOWPWD */ (void) time (&week); week /= (7L * 24L * 60L * 60L); /* weeks since epoch */ lastweek = a64l (&pwent.pw_age[2]); if (c64i (pwent.pw_age[0]) < c64i (pwent.pw_age[1])) { puts ("You may not change this password"); exit (1); } if (c64i (pwent.pw_age[1]) + lastweek > week) { printf ("Sorry, less than %d weeks since the last change\n", c64i (pwent.pw_age[1])); exit (1); } #ifdef SHADOWPWD } #endif } #endif printf ("Enter new password (minimum of %d characters)\n", PASSLENGTH); #ifdef OBSCURE puts ("Please use a combination of upper and lowercase letters and numbers"); #endif retries = RETRIES; retry: if (! password ("New Password:", pass)) exit (1); #ifndef OBSCURE if (! obscure ()) { puts ("Password not changed."); exit (1); } #else if (! force && ! obscure ()) { if (retries-- > 0) { puts ("Please try again."); goto retry; } else goto toomany; } #endif if (! password ("Re-enter new password:", pass2)) exit (1); if (strcmp (pass, pass2) != 0) { puts ("They don't match; try again"); if (retries-- > 0) goto retry; else goto toomany; } #ifdef AGING if (pwent.pw_age) { cp = l64a (week); pwent.pw_age[2] = cp[0]; pwent.pw_age[3] = cp[1]; pwent.pw_age[4] = '\0'; } #endif (void) time (&salttime); salttime = ((salttime & 07777) ^ ((salttime >> 14) & 07777)) & 07777; pwent.pw_passwd = tmp; strcpy (pwent.pw_passwd, crypt (pass, l64a (salttime))); #ifdef DOUBLESIZE if (strlen (pass) > 8) { strcpy (pwent.pw_passwd + 13, crypt (pass + 8, l64a (salttime)) + 2); } #endif /* * Now we get to race the bad guy. I don't think he can get us. * * Ignore most reasonable signals. * Maybe we should ignore more? He can't hurt us until the end. * * Get a lock file. * * Copy first part of password file to new file. * Illegal lines are copied verbatim. * File permissions are r--r--r--, owner root, group root. * * Output the new entry. * Only fields in struct passwd are output. * * Copy the rest of the file verbatim. * * Rename (link, unlink) password file to backup. * Kill me now and nothing changes or no one gets in. * * Rename (link, unlink) temporary file to password file. * Kill me now and no one gets in or lock is left. * * Remove locking file. * * That's all folks ... */ signal (SIGHUP, SIG_IGN); signal (SIGINT, SIG_IGN); signal (SIGQUIT, SIG_IGN); signal (SIGTERM, SIG_IGN); umask (0); /* get new files modes correct */ #ifndef NDEBUG if ((lockfd = open (".pwdlock", O_RDONLY|O_CREAT|O_EXCL), 0444) == -1) #else if ((lockfd = open (PWDLOCK, O_RDONLY|O_CREAT|O_EXCL), 0444) == -1) #endif /* NDEBUG */ { puts ("Can't get lock"); exit (1); } umask (077); /* close security holes to come ... */ #ifdef SHADOWPWD if (access (NSHADOW, 0) == 0 && unlink (NSHADOW) == -1) goto failure; if ((npwd = fopen (NSHADOW, "w")) == (FILE *) 0) goto failure; if (chmod (NSHADOW, 0400) || chown (NSHADOW, 0, 0)) goto failure; setspent (); while (spwd = getspent ()) { if (strcmp (spwd->sp_namp, name) == 0) break; if (putspent (spwd, npwd)) goto failure; } if (spwd == (struct spwd *) 0) { /* didn't find a match */ spwd = &tspwd; /* use a local structure instead */ spwd->sp_namp = pwent.pw_name; spwd->sp_max = 10000; /* about as big as possible */ spwd->sp_min = 0; /* about as small as possible */ } spwd->sp_pwdp = pwent.pw_passwd; /* fixup the password */ (void) time (&lastweek); /* get the current time ... */ lastweek /= (24L*60L*60L); /* ... turn it into days. */ spwd->sp_lstchg = lastweek; /* save it as date of last change */ (void) putspent (spwd, npwd); /* add the new entry */ while (spwd = getspent ()) /* finish the other ones off */ (void) putspent (spwd, npwd); endspent (); if (ferror (npwd)) { perror (NSHADOW); if (unlink (NPWDFILE) || unlink (PWDLOCK)) fputs ("Help!\n", stderr); exit (1); } fflush (npwd); fclose (npwd); if (access (OSHADOW, 0) == 0) { if (unlink (OSHADOW)) { puts ("Can't remove backup file"); goto unlock; } } if (link (SHADOW, OSHADOW) || unlink (SHADOW)) { puts ("Can't save backup file"); goto unlock; } #ifndef BSD if (link (NSHADOW, SHADOW) || unlink (NSHADOW)) #else if (rename (NSHADOW, SHADOW)) #endif { (void) unlink (OSHADOW); puts ("Can't rename new file"); goto unlock; } if (unlink (OSHADOW)) { puts ("Can't remove backup file"); goto unlock; } #else /* ! SHADOWPWD */ if (access (NPWDFILE, 0) == 0 && unlink (NPWDFILE) == -1) goto failure; #ifndef NDEBUG if ((npwd = fopen ("npasswd", "w")) == (FILE *) 0) #else umask (077); /* no permissions for non-roots */ if ((npwd = fopen (NPWDFILE, "w")) == (FILE *) 0) #endif /* NDEBUG */ goto failure; #ifndef NDEBUG chmod (NPWDFILE, 0444); /* lets have some security here ... */ chown (NPWDFILE, 0, 0); /* ... and keep the bad guy away */ #endif /* NDEBUG */ if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0) goto failure; while (fgets (buf, BUFSIZ, pwd) != (char *) 0) { if (buf[0] == '#' || ! (pw = sgetpwent (buf))) { fputs (buf, npwd); } else if (strcmp (pw->pw_name, pwent.pw_name) != 0) fputs (buf, npwd); else break; } (void) fprintf (npwd, "%s:", pw->pw_name); if (pwent.pw_age) (void) fprintf (npwd, "%s,%s:", pwent.pw_passwd, pwent.pw_age); else (void) fprintf (npwd, "%s:", pwent.pw_passwd); (void) fprintf (npwd, "%d:%d:%s:%s:%s\n", pwent.pw_uid, pwent.pw_gid, pwent.pw_gecos, pwent.pw_dir, pwent.pw_shell ? pwent.pw_shell:""); while (fgets (buf, BUFSIZ, pwd) != (char *) 0) fputs (buf, npwd); if (ferror (npwd)) { perror (NPWDFILE); if (unlink (NPWDFILE) || unlink (PWDLOCK)) fputs ("Help!\n", stderr); exit (1); } fflush (npwd); fclose (npwd); #ifdef NDEBUG chmod (NPWDFILE, 0644); if (unlink (OPWDFILE) == -1) { if (errno != ENOENT) { puts ("Can't unlink backup file"); goto unlock; } } if (link (PWDFILE, OPWDFILE) || unlink (PWDFILE)) { puts ("Can't save backup file"); goto unlock; } #ifndef BSD if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE)) #else if (rename (NPWDFILE, PWDFILE)) #endif { puts ("Can't rename new file"); goto unlock; } #endif /* NDEBUG */ #endif /* SHADOW */ #ifndef NDEBUG (void) unlink (".pwdlock"); #else (void) unlink (PWDLOCK); #endif exit (0); /*NOTREACHED*/ failure: puts ("Permission denied."); unlock: if (lockfd >= 0) (void) unlink (PWDLOCK); (void) unlink (NPWDFILE); exit (1); /*NOTREACHED*/ toomany: puts ("Too many tries; try again later."); exit (1); /*NOTREACHED*/ } /* * die - set or reset termio modes. * * die() is called before processing begins. signal() is then * called with die() as the signal handler. If signal later * calls die() with a signal number, the terminal modes are * then reset. */ void die (killed) int killed; { #ifdef BSD static struct sgtty sgtty; if (killed) stty (0, &sgtty); else gtty (0, &sgtty); #else struct termio sgtty; if (killed) ioctl (0, TCSETA, &sgtty); else ioctl (0, TCGETA, &sgtty); #endif if (killed) exit (killed); } SHAR_EOF fi if test -f 'sulogin.c' then echo shar: "will not over-write existing file 'sulogin.c'" else cat << \SHAR_EOF > 'sulogin.c' #include <sys/types.h> #include <stdio.h> #include <pwd.h> #include <utmp.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifndef lint static char _sccsid[] = "@(#)sulogin.c 2.1 01:24:02 6/20/89"; #endif char name[BUFSIZ]; char pass[BUFSIZ]; char home[BUFSIZ]; char prog[BUFSIZ]; char mail[BUFSIZ]; struct passwd pwent; struct utmp utent; #ifdef TZ FILE *tzfile; char tzbuf[16] = TZ; #endif #ifndef MAXENV #define MAXENV 64 #endif char *newenvp[MAXENV]; int newenvc = 0; int maxenv = MAXENV; extern char **environ; #ifndef ALARM #define ALARM 60 #endif #ifndef RETRIES #define RETRIES 3 #endif int main (argc, argv, envp) int argc; char **argv; char **envp; { char *getenv (); char *ttyname (); char *cp; if (access (PWDFILE, 0) == -1) { /* must be a password file! */ printf ("No password file\n"); exit (1); } #ifndef DEBUG if (getppid () != 1) /* parent must be INIT */ exit (1); #endif if (! isatty (0)) /* must be a terminal */ exit (1); while (*envp) /* add inherited environment, */ addenv (*envp++); /* some variables change later */ #ifdef TZ if (tzbuf[0] == '/') { if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) { if (fgets (tzbuf, sizeof tzbuf, tzfile)) { tzbuf[strlen (tzbuf) - 1] = '\0'; addenv (tzbuf); } fclose (tzfile); } } else { addenv (tzbuf); } #endif #ifdef HZ addenv (HZ); /* set the default $HZ, if one */ #endif (void) strcpy (name, "root"); /* KLUDGE!!! */ while (1) { /* repeatedly get login/password pairs */ entry (name, &pwent); /* get entry from password file */ if (pwent.pw_name == (char *) 0) { printf ("No password entry for 'root'\n"); exit (1); } /* * Here we prompt for the root password, or if no password is * given we just exit. */ /* get a password for root */ if (! password ("Type control-d for normal startup,\n(or give root password for system maintenance):", pass)) exit (0); if (valid (pass, &pwent)) /* check encrypted passwords ... */ break; /* ... encrypted passwords matched */ puts ("Login incorrect"); } environ = newenvp; /* make new environment active */ puts ("Entering System Maintenance Mode"); /* * Normally there would be a utmp entry for login to mung on * to get the tty name, date, etc. from. We don't need all that * stuff because we won't update the utmp or wtmp files. BUT!, * we do need the tty name so we can set the permissions and * ownership. */ if (cp = ttyname (0)) { /* found entry in /dev/ */ if (strrchr (cp, '/') != (char *) 0) strcpy (utent.ut_line, strrchr (cp, '/') + 1); else strcpy (utent.ut_line, cp); } if (getenv ("IFS")) /* don't export user IFS ... */ addenv ("IFS= \t\n"); /* ... instead, set a safe IFS */ setup (&pwent); /* set UID, GID, HOME, etc ... */ shell (pwent.pw_shell); /* exec the shell finally. */ /*NOTREACHED*/ } SHAR_EOF fi if test -f 'dialup.h' then echo shar: "will not over-write existing file 'dialup.h'" else cat << \SHAR_EOF > 'dialup.h' /* * Structure of d_passwd file * * The d_passwd file contains the names of login shells which require * dialup passwords. Each line contains the fully qualified path name * for the shell, followed by an optional password. Each field is * separated by a ':'. * * Structure of the dialups file * * The dialups file contains the names of ports which may be dialup * lines. Each line consists of the last component of the path * name. Any leading directory names are removed. * * @(#)dialup.h 2.1 01:23:08 6/20/89 */ struct dialup { char *du_shell; char *du_passwd; }; void setduent (); void endduent (); struct dialup *getduent (); struct dialup *getdushell (); #define DIALPWD "/etc/d_passwd" #define DIALUPS "/etc/dialups" SHAR_EOF fi if test -f 'ttytype.c' then echo shar: "will not over-write existing file 'ttytype.c'" else cat << \SHAR_EOF > 'ttytype.c' #include <stdio.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifdef TTYTYPE #ifndef lint static char _sccsid[] = "@(#)ttytype.c 2.1 01:24:04 6/20/89"; #endif /* * ttytype - set ttytype from port to terminal type mapping database */ void ttytype (line) char *line; { FILE *fp; char buf[BUFSIZ]; char termvar[BUFSIZ]; char *cp; char *type; char *port; char *getenv (); if (getenv ("TERM")) return; if (! (fp = fopen (TTYTYPE, "r"))) return; while (fgets (buf, BUFSIZ, fp)) { if (buf[0] == '#') continue; if (cp = strchr (buf, '\n')) *cp = '\0'; if ((type = strtok (buf, " \t")) && (port = strtok ((char *) 0, " \t"))) { if (strcmp (line, port) == 0) break; } } if (! feof (fp) && ! ferror (fp)) { strcat (strcpy (termvar, "TERM="), type); addenv (termvar); } fclose (fp); } #endif SHAR_EOF fi exit 0 # End of shell archive -- John F. Haugh II +-Button of the Week Club:------------- VoiceNet: (512) 832-8832 Data: -8835 | "AIX is a three letter word, InterNet: jfh@rpp386.Cactus.Org | and it's BLUE." UucpNet : <backbone>!bigtex!rpp386!jfh +--------------------------------------
jfh@rpp386.Dallas.TX.US (John F. Haugh II) (06/20/89)
X-Archive-Name: shadow2/part2 Part 2 of second USENET release -- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # lastlog.h # login.c # motd.c # password.c # shell.c # utmp.c # age.c # env.c # pwent.c # shadow.c # valid.c # lmain.c # smain.c # pwconv.c # dialup.c # dialchk.c # pwunconv.c # failure.c # faillog.h # faillog.c # This archive created: Tue Jun 20 01:24:59 1989 # By: John F. Haugh II (River Parishes Programming, Plano TX) export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'lastlog.h' then echo shar: "will not over-write existing file 'lastlog.h'" else cat << \SHAR_EOF > 'lastlog.h' /* * lastlog.h - structure of lastlog file * * This file defines a lastlog file structure which should be sufficient * to hold the information required by login. It should only be used if * there is no real lastlog.h file. */ struct lastlog { time_t ll_time; char ll_line[8]; }; SHAR_EOF fi if test -f 'login.c' then echo shar: "will not over-write existing file 'login.c'" else cat << \SHAR_EOF > 'login.c' #include <stdio.h> #include <ctype.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif void setenv (); void login (name) char *name; { char buf[BUFSIZ]; char *envp[32]; int envc; char *cp; int i; #ifndef BSD (void) memset (buf, '\0', sizeof buf); #else bzero (buf, sizeof buf); #endif fputs ("login: ", stdout); if (fgets (buf, BUFSIZ, stdin) != buf) exit (1); buf[strlen (buf) - 1] = '\0'; /* remove \n [ must be there ] */ for (cp = buf;*cp == ' ' || *cp == '\t';cp++) ; for (i = 0;i < BUFSIZ - 1 && isgraph (*cp);name[i++] = *cp++) ; if (*cp) cp++; name[i] = '\0'; if (*cp != '\0') { /* process new variables */ for (envc = 0;envc < 32;envc++) { envp[envc] = strtok (envc == 0 ? cp:(char *) 0, " \t,"); if (envp[envc] == (char *) 0) break; } setenv (envc, envp); } } SHAR_EOF fi if test -f 'motd.c' then echo shar: "will not over-write existing file 'motd.c'" else cat << \SHAR_EOF > 'motd.c' #include <stdio.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifndef lint static char _sccsid[] = "@(#)motd.c 2.1 01:23:27 6/20/89"; #endif extern char home[]; #ifdef HUSHLOGIN extern int hushed; #endif #ifdef MOTD void motd () { FILE *fp; register int c; #ifdef HUSHLOGIN if (hushed) return; #endif if ((fp = fopen ("/etc/motd", "r")) == (FILE *) 0) return; while ((c = getc (fp)) != EOF) putchar (c); fclose (fp); fflush (stdout); } #endif SHAR_EOF fi if test -f 'password.c' then echo shar: "will not over-write existing file 'password.c'" else cat << \SHAR_EOF > 'password.c' #include <stdio.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #ifndef BSD #include <termio.h> #else #include <sgtty.h> #endif #include <fcntl.h> /* * password - prompt for password and return entry * * Need to fake up getpass(). Returns TRUE if a password * was successfully input, and FALSE otherwise, including * EOF on input or ioctl() failure. pass is not modified * on failure. The input length limit may be set by * changing the value of PASSLIMIT. */ #ifndef lint static char _sccsid[] = "@(#)password.c 2.1 01:23:33 6/20/89"; #endif #define PASSLIMIT 20 int password (prompt, pass) char *prompt; char *pass; { char buf[BUFSIZ]; char *cp; int eof; int ttyopened = 0; #ifndef BSD struct termio termio; struct termio save; #else struct sgttyb termio ; struct sgttyb save ; #endif FILE *fp; if ((fp = fopen ("/dev/tty", "r")) == (FILE *) 0) fp = stdin; else ttyopened = 1; #ifndef BSD if (ioctl (fileno (fp), TCGETA, &termio)) return (0); #else if ( gtty( fileno(fp), &termio ) ) return (0); #endif save = termio; #ifndef BSD termio.c_lflag &= ~(ECHO|ECHOE|ECHOK); ioctl (fileno (fp), TCSETAF, &termio); #else termio.sg_flags &= ~ECHO ; stty( fileno( fp ), termio ) ; #endif fputs (prompt, stdout); eof = fgets (buf, BUFSIZ, fp) == (char *) 0 || feof (fp) || ferror (fp); putchar ('\n'); #ifndef BSD ioctl (fileno (fp), TCSETAF, &save); #else stty( fileno( fp ), save ) ; #endif if (! eof) { buf[PASSLIMIT] = '\0'; if ((cp = strchr (buf, '\n')) || (cp = strchr (buf, '\r'))) *cp = '\0'; (void) strcpy (pass, buf); } if (ttyopened) fclose (fp); return (! eof); } SHAR_EOF fi if test -f 'shell.c' then echo shar: "will not over-write existing file 'shell.c'" else cat << \SHAR_EOF > 'shell.c' #include <stdio.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifndef lint static char _sccsid[] = "@(#)shell.c 2.1 01:23:53 6/20/89"; #endif extern char *newenvp[]; void shell (file) char *file; { char arg0[BUFSIZ]; char *path; extern int errno; if (file == (char *) 0) exit (1); if (path = strrchr (file, '/')) path++; else path = file; (void) strcpy (arg0 + 1, path); arg0[0] = '-'; #ifndef NDEBUG printf ("Executing shell %s\n", file); #endif execle (file, arg0, (char *) 0, newenvp); printf ("Can't execute %s\n", file); exit (errno); } SHAR_EOF fi if test -f 'utmp.c' then echo shar: "will not over-write existing file 'utmp.c'" else cat << \SHAR_EOF > 'utmp.c' #include <sys/types.h> #include <utmp.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include <stdio.h> #include "config.h" extern struct utmp utent; extern char name[]; struct utmp *getutent (); void setutent (); void endutent (); void pututline (); time_t time (); void checkutmp () { struct utmp *ut; #ifndef NDEBUG int pid = getppid (); #else int pid = getpid (); #endif setutent (); while (ut = getutent ()) if (ut->ut_pid == pid) break; if (ut) utent = *ut; endutent (); if (ut && utent.ut_pid == pid) return; puts ("No utmp entry. You must exec \"login\" from the lowest level \"sh\""); exit (1); } void setutmp () { FILE *wtmp; char tty[sizeof utent.ut_line + 1]; char *line; setutent (); (void) strncpy (utent.ut_user, name, sizeof utent.ut_user); utent.ut_type = USER_PROCESS; if (line = strrchr (utent.ut_line, '/')) { (void) strcpy (tty, line + 1); #ifndef BSD (void) memset (utent.ut_line, '\0', sizeof utent.ut_line); #else bzero (utent.ut_line, sizeof utent.ut_line); #endif (void) strcpy (utent.ut_line, tty); } (void) time (&utent.ut_time); pututline (&utent); endutent (); if ((wtmp = fopen (WTMP_FILE, "a+"))) { fwrite (&utent, sizeof utent, 1, wtmp); fclose (wtmp); } } SHAR_EOF fi if test -f 'age.c' then echo shar: "will not over-write existing file 'age.c'" else cat << \SHAR_EOF > 'age.c' #include <sys/types.h> #include <stdio.h> #include <pwd.h> #include "config.h" #ifndef lint static char _sccsid[] = "@(#)age.c 2.11 01:23:02 6/20/89"; #endif #ifndef PASSWD extern char *newenvp[]; #endif time_t time (); int c64i (c) char c; { if (c == '.') return (0); if (c == '/') return (1); if (c >= '0' && c <= '9') return (c - '0' + 2); if (c >= 'A' && c <= 'Z') return (c - 'A' + 12); if (c >= 'a' && c <= 'z') return (c - 'a' + 38); else return (-1); } #ifdef AGING #ifdef PASSWD #ifdef NEED_AL64 char *l64a (l) long l; { static char buf[8]; int i = 0; if (i < 0L) return ((char *) 0); do { buf[i++] = i64c ((int) (l % 64)); buf[i] = '\0'; } while (l /= 64L, l > 0 && i < 6); return (buf); } #endif #endif int i64c (i) int i; { if (i < 0) return ('.'); else if (i > 63) return ('z'); if (i == 0) return ('.'); if (i == 1) return ('/'); if (i >= 2 && i <= 11) return ('0' - 2 + i); if (i >= 12 && i <= 37) return ('A' - 12 + i); if (i >= 38 && i <= 63) return ('a' - 38 + i); return ('\0'); } #ifdef NEED_AL64 long a64l (s) char *s; { int i; long value; long shift = 0; for (i = 0, value = 0L;i < 6 && *s;s++) { value += (c64i (*s) << shift); shift += 6; } return (value); } #endif #ifndef PASSWD void expire (age) char *age; { long clock; long week; extern char name[]; extern int errno; (void) time (&clock); clock /= (7L * 24L * 60L * 60L); if (strlen (age) < 4) week = 0L; else week = a64l (age + 2); if (clock >= week + c64i (age[0])) { printf ("Your password has expired."); if (c64i (age[0]) < c64i (age[1])) { puts (" Contact the system administrator.\n"); exit (1); } puts (" Choose a new one.\n"); execl ("/bin/passwd", "-passwd", name, (char *) 0); puts ("Can't execute /bin/passwd"); exit (errno); } } #endif #endif SHAR_EOF fi if test -f 'env.c' then echo shar: "will not over-write existing file 'env.c'" else cat << \SHAR_EOF > 'env.c' #include <stdio.h> #ifndef BSD #include <string.h> #else #define strchr index #define strrchr rindex #include <strings.h> #endif #ifndef lint static char _sccsid[] = "@(#)env.c 2.1 01:23:11 6/20/89"; #endif extern char **environ; extern char *newenvp[]; extern int newenvc; extern int maxenv; char *strdup (); void free (); static char *forbid[] = { "HOME", "IFS", "PATH", "SHELL", (char *) 0 }; void addenv (entry) char *entry; { char *cp; int i; int len; if (cp = strchr (entry, '=')) len = cp - entry; else return; for (i = 0;i < newenvc;i++) if (strncmp (entry, newenvp[i], len) == 0 && (newenvp[i][len] == '=' || newenvp[i][len] == '\0')) break; if (i == maxenv) { puts ("Environment overflow"); return; } if (i == newenvc) { newenvp[newenvc++] = strdup (entry); } else { free (newenvp[i]); newenvp[i] = strdup (entry); } } void setenv (argc, argv) int argc; char **argv; { int i; int n; int noname = 1; char variable[BUFSIZ]; char *cp; for (i = 0;i < argc;i++) { if ((n = strlen (argv[i])) >= BUFSIZ) continue; /* ignore long entries */ if (! (cp = strchr (argv[i], '='))) { (void) strcpy (variable, argv[i]); } else { (void) strncpy (variable, argv[i], cp - argv[i]); variable[cp - argv[i]] = '\0'; } for (n = 0;forbid[n] != (char *) 0;n++) if (strcmp (variable, forbid[n]) == 0) break; if (forbid[n] != (char *) 0) { printf ("You may not change $%s\n", forbid[n]); continue; } if (cp) { addenv (argv[i]); } else { sprintf (variable, "L%d=%s", noname++, argv[i]); addenv (variable); } } } SHAR_EOF fi if test -f 'pwent.c' then echo shar: "will not over-write existing file 'pwent.c'" else cat << \SHAR_EOF > 'pwent.c' #include <stdio.h> #include <pwd.h> #include <string.h> #include "config.h" #ifndef lint static char _sccsid[] = "@(#)pwent.c 2.1 01:23:41 6/20/89"; #endif #define SBUFSIZ 64 #define NFIELDS 7 static char pwdbuf[BUFSIZ]; static char *pwdfields[NFIELDS]; struct passwd *sgetpwent (buf) char *buf; { int i; char *cp; static struct passwd pwent; strncpy (pwdbuf, buf, BUFSIZ); pwdbuf[BUFSIZ-1] = '\0'; cp = pwdbuf; for (i = 0;i < NFIELDS && cp;i++) { pwdfields[i] = cp; if (cp = strchr (cp, ':')) *cp++ = 0; } if (i < (NFIELDS-1) || *pwdfields[2] == '\0' || *pwdfields[3] == '\0') return ((struct passwd *) 0); for (;i < NFIELDS;i++) pwdfields[i] = 0; pwent.pw_name = pwdfields[0]; pwent.pw_passwd = pwdfields[1]; pwent.pw_uid = atoi (pwdfields[2]); pwent.pw_gid = atoi (pwdfields[3]); if (cp = strchr (pwent.pw_passwd, ',')) { pwent.pw_age = cp + 1; *cp = '\0'; } pwent.pw_gecos = pwdfields[4]; pwent.pw_dir = pwdfields[5]; pwent.pw_shell = pwdfields[6]; return (&pwent); } #ifdef FGETPWENT struct passwd *fgetpwent (fp) FILE *fp; { char buf[BUFSIZ]; while (fgets (buf, BUFSIZ, fp) != (char *) 0) { if (buf[0] == '#') continue; buf[strlen (buf) - 1] = '\0'; return (sgetpwent (buf)); } return ((struct passwd *) 0); } #endif SHAR_EOF fi if test -f 'shadow.c' then echo shar: "will not over-write existing file 'shadow.c'" else cat << \SHAR_EOF > 'shadow.c' #include "shadow.h" #include <stdio.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #ifndef lint static char _sccsid[] = "@(#)shadow.c 2.1 01:23:49 6/20/89"; #endif static FILE *shadow; void setspent () { if (shadow) rewind (shadow); else shadow = fopen (SHADOW, "r"); } void endspent () { if (shadow) (void) fclose (shadow); shadow = (FILE *) 0; } struct spwd *fgetspent (fp) FILE *fp; { static struct spwd spwd; static char name[32]; static char pass[32]; char buf[BUFSIZ]; char *cp; int atoi (); long atol (); if (! fp) return (0); if (fgets (buf, BUFSIZ, fp) == (char *) 0) return (0); buf[strlen (buf) - 1] = '\0'; if ((cp = strtok (buf, ":")) && *cp) (void) strcpy (name, cp); else return (0); if ((cp = strtok ((char *) 0, ":")) && *cp) (void) strcpy (pass, cp); else return (0); if ((cp = strtok ((char *) 0, ":")) && *cp) spwd.sp_lstchg = atol (cp); else return (0); if ((cp = strtok ((char *) 0, ":")) && *cp) spwd.sp_min = atoi (cp); else return (0); if ((cp = strtok ((char *) 0, ":")) && *cp) spwd.sp_max = atoi (cp); else return (0); spwd.sp_namp = name; spwd.sp_pwdp = pass; return (&spwd); } struct spwd *getspent () { if (! shadow) setspent (); return (fgetspent (shadow)); } struct spwd *getspnam (name) char *name; { struct spwd *spwd; setspent (); while ((spwd = getspent ()) != (struct spwd *) 0) { if (strcmp (name, spwd->sp_namp) == 0) return (spwd); } return (0); } int putspent (spwd, fp) struct spwd *spwd; FILE *fp; { if (! fp) return (0); return (fprintf (fp, "%s:%s:%ld:%ld:%ld\n", spwd->sp_namp, spwd->sp_pwdp, spwd->sp_lstchg, spwd->sp_min, spwd->sp_max) != EOF); } SHAR_EOF fi if test -f 'valid.c' then echo shar: "will not over-write existing file 'valid.c'" else cat << \SHAR_EOF > 'valid.c' #include <stdio.h> #include <pwd.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #ifndef lint static char _sccsid[] = "@(#)valid.c 2.1 01:24:08 6/20/89"; #endif /* * valid - compare encrypted passwords * * Valid() compares the DES encrypted password from the password file * against the password which the user has entered after it has been * encrypted using the same salt as the original. */ int valid (password, entry) char *password; struct passwd *entry; { char *encrypt; char *salt; char *crypt (); #ifdef DOUBLESIZE int firsthalf; int longpass; #endif /* * Start with blank or empty password entries. Always encrypt * a password if no such user exists. Only if the ID exists and * the password is really empty do you return quickly. This * routine is meant to waste CPU time. */ if (entry->pw_name && (entry->pw_passwd == (char *) 0 || strlen (entry->pw_passwd) == 0)) { if (strlen (password) == 0) return (1); /* user entered nothing */ else return (0); /* user entered something! */ } #ifdef DOUBLESIZE longpass = entry->pw_passwd && strlen (entry->pw_passwd) > 13; #endif /* * /* * If there is no entry then we need a salt to use. */ if (entry->pw_passwd == (char *) 0 || entry->pw_passwd[0] == '\0') salt = "xx"; else salt = entry->pw_passwd; /* * Now, perform the encryption using the salt from before on * the users input. Since we always encrypt the string, it * should be very difficult to determine if the user exists by * looking at execution time. */ encrypt = crypt (password, salt); #ifdef DOUBLESIZE firsthalf = entry->pw_passwd && strncmp (encrypt + 2, entry->pw_passwd + 2, 11) == 0; if (strlen (password) > 8) encrypt = crypt (password + 8, salt); else { (void) crypt (password, salt); /* waste time ... */ encrypt = ""; } #endif /* * One last time we must deal with there being no password file * entry for the user. We use the pw_passwd == NULL idiom to * cause non-existent users to not be validated. Even still, * we are safe because if the string were == "", any encrypted * string is not going to match - the output of crypt() begins * with the salt, which is "xx", not "". */ #ifdef NOUSE if (entry->pw_shell && strcmp ("NO_USE", entry->pw_shell) == 0) return (0); #endif #ifndef DOUBLESIZE if (entry->pw_passwd && strcmp (encrypt, entry->pw_passwd) == 0) return (1); else return (0); #else if (! longpass) return (firsthalf); if (entry->pw_passwd && firsthalf && strncmp (encrypt + 2, entry->pw_passwd + 13) == 0) return (1); else return (0); #endif } SHAR_EOF fi if test -f 'lmain.c' then echo shar: "will not over-write existing file 'lmain.c'" else cat << \SHAR_EOF > 'lmain.c' #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <pwd.h> #include <utmp.h> #include <time.h> #include <signal.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #ifndef BSD #include <termio.h> #else #include <sgtty.h> #endif #include "config.h" #include "lastlog.h" #include "faillog.h" #ifndef lint static char _sccsid[] = "@(#)lmain.c 2.1 01:23:18 6/20/89"; #endif #ifndef ERASECHAR #define ERASECHAR '\b' /* backspace */ #endif #ifndef KILLCHAR #define KILLCHAR '\025' /* control U */ #endif char name[BUFSIZ]; char pass[BUFSIZ]; char home[BUFSIZ]; char prog[BUFSIZ]; char mail[BUFSIZ]; #ifdef HUSHLOGIN char hush[BUFSIZ]; int hushed; #endif struct passwd pwent; struct utmp utent; struct lastlog lastlog; #ifndef BSD struct termio termio; #endif #ifndef MAXENV #define MAXENV 64 #endif char *newenvp[MAXENV]; int newenvc = 0; int maxenv = MAXENV; extern char **environ; char *getenv (); void checkutmp (); void addenv (); void setenv (); unsigned alarm (); void login (); void entry (); void setutmp (); void subsystem (); void log (); void setup (); void expire (); void motd (); void mailcheck (); void shell (); #ifdef TZ FILE *tzfile; char tzbuf[32] = TZ; #endif #ifndef ALARM #define ALARM 60 #endif #ifndef RETRIES #define RETRIES 3 #endif #ifdef FAILLOG struct faillog faillog; #endif int main (argc, argv, envp) int argc; char **argv; char **envp; { int retries = RETRIES; int failed; #ifdef CONSOLE int conflag; char console[BUFSIZ]; FILE *fp; struct stat statbuf; #endif checkutmp (); /* must be lowest level shell */ if (! isatty (0)) /* must be a terminal */ exit (1); #ifndef BSD (void) ioctl (0, TCGETA, &termio); /* get terminal characteristics */ /* * Add your favorite terminal modes here ... */ termio.c_lflag |= ISIG; termio.c_cc[VERASE] = ERASECHAR; termio.c_cc[VKILL] = KILLCHAR; (void) ioctl (0, TCSETAF, &termio); /* set erase and kill characters */ #endif #ifdef UMASK umask (UMASK); /* override the default umask */ #endif #ifdef ULIMIT ulimit (2, (long) ULIMIT); /* override the default ulimit */ #endif while (*envp) /* add inherited environment, */ addenv (*envp++); /* some variables change later */ #ifdef TZ if (tzbuf[0] == '/') { if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) { if (fgets (tzbuf, sizeof tzbuf, tzfile)) { tzbuf[strlen (tzbuf) - 1] = '\0'; addenv (tzbuf); } fclose (tzfile); } } else { addenv (tzbuf); } #endif #ifdef HZ addenv (HZ); /* set the default $HZ, if one */ #endif if (argc >= 2) { /* now set command line variables */ setenv (argc - 2, &argv[2]); (void) strncpy (name, argv[1], sizeof name); } (void) alarm (ALARM); /* only allow ALARM sec. for login */ while (1) { /* repeatedly get login/password pairs */ if (! name[0]) { /* need to get a login id */ login (name); continue; } entry (name, &pwent); /* get entry from password file */ failed = 0; /* hasn't failed validation yet */ /* * Here we have a sticky situation. Some accounts may have no * password entry in the password file. So, we don't ask for a * password. Others, have a blank password entered - you be the * judge. The conditional compilation NOBLANK requires even * blank passwords to be prompted for. This may well break * quite a few systems. Use with discretion. */ #ifdef NOBLANK /* get a password from user */ if (! password ("Password:", pass)) continue; #else if ((! pwent.pw_name || pwent.pw_passwd) && ! password ("Password:", pass)) continue; #endif if (! valid (pass, &pwent)) /* check encrypted passwords ... */ failed = 1; #ifdef DIALUP alarm (30); if (! dialcheck (utent.ut_line, pwent.pw_shell ? pwent.pw_shell:"/bin/sh")) failed = 1; #endif #ifdef CONSOLE if (pwent.pw_uid == 0 && stat (CONSOLE, &statbuf) == 0) { if ((statbuf.st_mode & S_IFMT) == S_IFREG) { fp = fopen (CONSOLE, "r"); while (fp && fgets (console, BUFSIZ, fp) == console) { console[strlen (console) - 1] = '\0'; if (! strcmp (console, utent.ut_line)) break; } if (! fp || feof (fp)) failed = 1; fclose (fp); } else { if (strcmp (CONSOLE, utent.ut_line)) failed = 1; } } #endif #ifdef FAILLOG if (! failcheck (pwent.pw_uid, &faillog, failed)) failed = 1; #endif if (! failed) break; puts ("Login incorrect"); #ifdef FAILLOG if (pwent.pw_name) /* don't log non-existent users */ failure (pwent.pw_uid, utent.ut_line, &faillog); #endif if (--retries <= 0) /* only allow so many failures */ exit (1); #ifndef BSD (void) memset (name, '\0', sizeof name); (void) memset (pass, '\0', sizeof pass); #else bzero (name, sizeof name); bzero (pass, sizeof pass); #endif } (void) alarm (0); /* turn off alarm clock */ #ifdef NOLOGINS /* * Check to see if system is turned off for non-root users. * This would be useful to prevent users from logging in * during system maintenance. */ if (pwent.pw_uid != 0 && access (NOLOGINS, 0) == 0) { printf ("\r\nSystem closed for routine maintenance\n"); exit (0); } #endif environ = newenvp; /* make new environment active */ if (getenv ("IFS")) /* don't export user IFS ... */ addenv ("IFS= \t\n"); /* ... instead, set a safe IFS */ setutmp (); /* make entry in utmp & wtmp files */ if (pwent.pw_shell && pwent.pw_shell[0] == '*') /* subsystem root */ subsystem (); /* figure out what to execute */ #ifdef LASTLOG log (); /* give last login and log this one */ #endif setup (&pwent); /* set UID, GID, HOME, etc ... */ #ifdef AGING if (pwent.pw_age) /* check for age of password ... */ expire (pwent.pw_age); /* ... ask for new one if expired */ #endif #ifdef HUSHLOGIN sprintf (hush, "%s/.hushlogin", strchr (home, '=') + 1); hushed = access (hush, 0) != -1; #endif #ifdef MOTD motd (); /* print the message of the day */ #endif #ifdef FAILLOG if (faillog.fail_cnt != 0) failprint (pwent.pw_uid, &faillog); #endif #ifdef LASTLOG if (lastlog.ll_time != 0 && ! hushed) printf ("Last login: %.19s on %s\n", ctime (&lastlog.ll_time), lastlog.ll_line); #endif #ifdef MAILCHECK mailcheck (); /* report on the status of mail */ #endif #ifdef TTYTYPE ttytype (utent.ut_line); #endif signal (SIGINT, SIG_DFL); /* default interrupt signal */ signal (SIGQUIT, SIG_DFL); /* default quit signal */ signal (SIGTERM, SIG_DFL); /* default terminate signal */ signal (SIGALRM, SIG_DFL); /* default alarm signal */ shell (pwent.pw_shell); /* exec the shell finally. */ /*NOTREACHED*/ } SHAR_EOF fi if test -f 'smain.c' then echo shar: "will not over-write existing file 'smain.c'" else cat << \SHAR_EOF > 'smain.c' #include <sys/types.h> #include <stdio.h> #include <pwd.h> #ifndef BSD #include <string.h> #include <memory.h> #include <termio.h> #else #include <strings.h> #include <sgtty.h> #define strchr index #define strrchr rindex #endif #include <signal.h> #include "config.h" #include "lastlog.h" #ifndef lint static char _sccsid[] = "@(#)smain.c 2.1 01:23:55 6/20/89"; #endif #ifndef MAXENV #define MAXENV 64 #endif #ifndef PATH #define PATH ":/bin:/usr/bin" #endif #ifndef SUPATH #define SUPATH ":/bin:/usr/bin:/etc" #endif #ifdef HUSHLOGIN char hush[BUFSIZ]; int hushed; #endif char name[BUFSIZ]; char pass[BUFSIZ]; char home[BUFSIZ]; char prog[BUFSIZ]; char mail[BUFSIZ]; char oldname[BUFSIZ]; char *newenvp[MAXENV]; int newenvc = 0; int maxenv = MAXENV; struct passwd pwent; #ifdef TZ FILE *tzfile; char tzbuf[16] = TZ; #endif void addenv (); void entry (); void sulog (); void subsystem (); void setup (); void motd (); void mailcheck (); void shell (); extern char **environ; int main (argc, argv, envp) int argc; char **argv; char **envp; { void die (); char *getenv (); char *cp; int doshell; int fakelogin = 0; int amroot; struct passwd *pw; struct passwd *getpwuid (); while (*envp) /* add inherited environment, */ addenv (*envp++); /* some variables change later */ #ifdef TZ if (tzbuf[0] == '/') { if ((tzfile = fopen (tzbuf, "r")) != (FILE *) 0) { if (fgets (tzbuf, sizeof tzbuf, tzfile)) { tzbuf[strlen (tzbuf) - 1] = '\0'; addenv (tzbuf); } fclose (tzfile); } } else { addenv (tzbuf); } #endif #ifdef HZ addenv (HZ); /* set the default $HZ, if one */ #endif argc--; argv++; /* shift out command name */ if (argc > 0 && argv[0][0] == '-' && argv[0][1] == '\0') { fakelogin = 1; argc--; argv++; /* shift ... */ } if (argc > 0 && argv[0][0] != '-') { (void) strcpy (name, argv[0]); /* use this login id */ argc--; argv++; /* shift ... */ } doshell = argc == 0; /* any arguments remaining? */ if (pw = getpwuid (getuid ())) /* need old user name */ (void) strcpy (oldname, pw->pw_name); else /* user ID MUST exist */ goto failure; amroot = getuid () == 0; /* currently am super user */ if (! name[0]) /* use default user ID */ (void) strcpy (name, "root"); entry (name, &pwent); /* get password file entry */ if (pwent.pw_shell == (char *) 0) pwent.pw_shell = "/bin/sh"; if (pwent.pw_name == (char *) 0) { /* unknown user */ (void) fprintf (stderr, "Unknown id: %s\n", name); exit (1); } /* * Here we have a sticky situation. Some accounts may have no * password entry in the password file. So, we don't ask for a * password. Others, have a blank password entered - you be the * judge. The conditional compilation NOBLANK requires even * blank passwords to be prompted for. This may well break * quite a few systems. Use with discretion. */ die (0); signal (SIGHUP, die); signal (SIGINT, die); signal (SIGQUIT, die); signal (SIGTERM, die); #ifdef NOBLANK if (! amroot && ! password ("Password:", pass)) goto failure; #else if (! amroot && (pwent.pw_name == (char *) 0 || pwent.pw_passwd) && ! password ("Password:", pass)) goto failure; #endif /* check encrypted passwords ... */ if (! amroot && ! valid (pass, &pwent)) { failure: sulog (0); /* log failed attempt */ puts ("Sorry."); exit (1); } signal (SIGHUP, SIG_DFL); signal (SIGINT, SIG_DFL); signal (SIGQUIT, SIG_DFL); signal (SIGTERM, SIG_DFL); #ifdef SULOG sulog (1); /* save SU information */ #endif if (pwent.pw_uid == 0) addenv (SUPATH); else addenv (PATH); environ = newenvp; /* make new environment active */ if (getenv ("IFS")) /* don't export user IFS ... */ addenv ("IFS= \t\n"); /* ... instead, set a safe IFS */ if (doshell && pwent.pw_shell[0] == '*') /* subsystem root required */ subsystem (); /* figure out what to execute */ if (fakelogin) setup (&pwent); /* set UID, GID, HOME, etc ... */ else { if (setgid (pwent.pw_gid) || setuid (pwent.pw_uid)) { perror ("Can't set ID"); exit (1); } } if (! doshell) { /* execute arguments as command */ if (cp = getenv ("SHELL")) pwent.pw_shell = cp; argv[-1] = pwent.pw_shell; (void) execv (pwent.pw_shell, &argv[-1]); (void) fprintf (stderr, "No shell\n"); exit (1); } if (fakelogin) { #ifdef HUSHLOGIN sprintf (hush, "%s/.hushlogin", strchr (home, '=') + 1); hushed = access (hush, 0) != -1; #endif #ifdef MOTD motd (); /* print the message of the day */ #endif #ifdef MAILCHECK mailcheck (); /* report on the status of mail */ #endif shell (pwent.pw_shell); /* exec the shell finally. */ } else { if (cp = strrchr (pwent.pw_shell, '/')) cp++; else cp = pwent.pw_shell; execl (pwent.pw_shell, cp, (char *) 0); perror (pwent.pw_shell); exit (1); } /*NOTREACHED*/ } /* * die - set or reset termio modes. * * die() is called before processing begins. signal() is then * called with die() as the signal handler. If signal later * calls die() with a signal number, the terminal modes are * then reset. */ void die (killed) int killed; { #ifdef BSD static struct sgtty sgtty; if (killed) stty (0, &sgtty); else gtty (0, &sgtty); #else struct termio sgtty; if (killed) ioctl (0, TCSETA, &sgtty); else ioctl (0, TCGETA, &sgtty); #endif if (killed) exit (killed); } SHAR_EOF fi if test -f 'pwconv.c' then echo shar: "will not over-write existing file 'pwconv.c'" else cat << \SHAR_EOF > 'pwconv.c' /* * pwconv - convert and update shadow password files * * Pwconv copies the old password file information to a new shadow * password file, merging entries from an optional existing shadow * file. * * The new password file is left in npasswd, the new shadow file is * left in nshadow. Existing shadow entries are copied as is. * New entries are created with passwords which expire in MAXDAYS days, * with a last changed date of today, unless password aging * information was already present. Likewise, the minimum number of * days before which the password may be changed is controlled by * MINDAYS. Entries with blank passwordsare not copied to the shadow * file at all. */ #include <sys/types.h> #include <stdio.h> #include <fcntl.h> #include <pwd.h> #include "config.h" #include "shadow.h" #ifndef lint static char _sccsid[] = "@(#)pwconv.c 2.1 01:23:40 6/20/89"; #endif char buf[BUFSIZ]; long time (); long a64l (); int main () { long today; struct passwd *pw; struct passwd *sgetpwent (); FILE *pwd; FILE *npwd; FILE *shadow; struct spwd *spwd; struct spwd tspwd; int fd; if (! (pwd = fopen (PWDFILE, "r"))) { perror (PWDFILE); return (1); } unlink ("npasswd"); if ((fd = open ("npasswd", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 || ! (npwd = fdopen (fd, "w"))) { perror ("npasswd"); return (1); } unlink ("nshadow"); if ((fd = open ("nshadow", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 || ! (shadow = fdopen (fd, "w"))) { perror ("nshadow"); return (1); } (void) time (&today); today /= (24L * 60L * 60L); while (fgets (buf, BUFSIZ, pwd) == buf) { buf[strlen (buf) - 1] = '\0'; /* remove '\n' character */ if (buf[0] == '#') { /* comment line */ (void) fprintf (npwd, "%s\n", buf); continue; } if (! (pw = sgetpwent (buf))) { /* copy bad lines verbatim */ (void) fprintf (npwd, "%s\n", buf); continue; } if (pw->pw_passwd == (char *) 0) { /* no password, skip */ (void) fprintf (npwd, "%s\n", buf); continue; } setspent (); /* rewind old shadow file */ if (spwd = getspnam (pw->pw_name)) { if (! putspent (spwd, shadow)) { /* copy old entry */ perror ("nshadow"); return (1); } } else { /* need a new entry. */ tspwd.sp_namp = pw->pw_name; tspwd.sp_pwdp = pw->pw_passwd; pw->pw_passwd = "x"; if (pw->pw_age) { /* copy old password age stuff */ tspwd.sp_min = c64i (pw->pw_age[1]); tspwd.sp_max = c64i (pw->pw_age[0]); if (strlen (pw->pw_age) == 4) tspwd.sp_lstchg = a64l (&pw->pw_age[2]); else tspwd.sp_lstchg = 0L; /* * Convert weeks to days */ tspwd.sp_min *= 7; tspwd.sp_max *= 7; tspwd.sp_lstchg *= 7; } else { /* fake up new password age stuff */ tspwd.sp_max = MAXDAYS; tspwd.sp_min = MINDAYS; tspwd.sp_lstchg = today; } if (! putspent (&tspwd, shadow)) { /* output entry */ perror ("nshadow"); return (1); } } (void) fprintf (npwd, "%s:%s:%d:%d:%s:%s:", pw->pw_name, pw->pw_passwd ? pw->pw_passwd:"", pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir); if (fprintf (npwd, "%s\n", pw->pw_shell ? pw->pw_shell:"") == EOF) { perror ("npasswd"); return (1); } } endspent (); if (ferror (npwd) || ferror (shadow)) { perror ("pwconv"); (void) unlink ("npasswd"); (void) unlink ("nshadow"); } (void) fclose (pwd); (void) fclose (npwd); (void) fclose (shadow); return (0); } SHAR_EOF fi if test -f 'dialup.c' then echo shar: "will not over-write existing file 'dialup.c'" else cat << \SHAR_EOF > 'dialup.c' #include <stdio.h> #ifndef BSD #include <string.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "dialup.h" #ifndef lint static char _sccsid[] = "@(#)dialup.c 2.1 01:23:06 6/20/89"; #endif static FILE *dialpwd; void setduent () { if (dialpwd) rewind (dialpwd); else dialpwd = fopen (DIALPWD, "r"); } void endduent () { if (dialpwd) fclose (dialpwd); dialpwd = (FILE *) 0; } struct dialup *getduent () { static struct dialup dialup; /* static structure to point to */ static char shell[64]; /* some space for a login shell */ static char passwd[16]; /* some space for dialup password */ char buf[BUFSIZ]; char *cp; if (! dialpwd) setduent (); if (! dialpwd || feof (dialpwd)) return ((struct dialup *) 0); while (fgets (buf, BUFSIZ, dialpwd) == buf && buf[0] == '#') ; if (feof (dialpwd)) return ((struct dialup *) 0); cp = strchr (buf, ':'); if (cp - buf > sizeof shell) /* something is fishy ... */ return ((struct dialup *) 0); (void) strncpy (shell, buf, cp - buf); shell[cp - buf] = '\0'; if (strlen (cp + 1) > sizeof passwd) /* something is REALLY fishy */ return ((struct dialup *) 0); (void) strcpy (passwd, cp + 1); passwd[strlen (passwd) - 1] = '\0'; if (cp = strchr (passwd, ':')) *cp = '\0'; dialup.du_shell = shell; dialup.du_passwd = passwd; return (&dialup); } struct dialup *getdushell (shell) char *shell; { struct dialup *dialup; while (dialup = getduent ()) { if (strcmp (shell, dialup->du_shell) == 0) return (dialup); if (strcmp (dialup->du_shell, "*") == 0) return (dialup); } return ((struct dialup *) 0); } int isadialup (tty) char *tty; { FILE *fp; char buf[BUFSIZ]; int dialup = 0; if (! (fp = fopen (DIALUPS, "r"))) return (0); while (fgets (buf, BUFSIZ, fp) == buf) { if (buf[0] == '#') continue; buf[strlen (buf) - 1] = '\0'; if (strcmp (buf, tty) == 0) { dialup = 1; break; } } fclose (fp); return (dialup); } SHAR_EOF fi if test -f 'dialchk.c' then echo shar: "will not over-write existing file 'dialchk.c'" else cat << \SHAR_EOF > 'dialchk.c' #include <stdio.h> #include "config.h" #include "dialup.h" #ifndef lint static char _sccsid[] = "@(#)dialchk.c 2.1 01:23:05 6/20/89"; #endif /* * Check for dialup password * * dialcheck tests to see if tty is listed as being a dialup * line. If so, a dialup password may be required if the shell * is listed as one which requires a second password. */ #ifdef DIALUP int dialcheck (tty, shell) char *tty; char *shell; { char *crypt (); char *getpass (); struct dialup *dialup; char *pass; char *cp; if (! isadialup (tty)) return (1); if (! (dialup = getdushell (shell))) return (1); endduent (); if (dialup->du_passwd[0] == '\0') return (1); if (! (pass = getpass ("Dialup Password:"))) return (0); cp = crypt (pass, dialup->du_passwd); return (strcmp (cp, dialup->du_passwd) == 0); } #endif SHAR_EOF fi if test -f 'pwunconv.c' then echo shar: "will not over-write existing file 'pwunconv.c'" else cat << \SHAR_EOF > 'pwunconv.c' /* * pwunconv - restore old password file from shadow password file. * * Pwunconv copies the password file information from the shadow * password file, merging entries from an optional existing shadow * file. * * The new password file is left in npasswd. There is no new * shadow file. Password aging information is translated where * possible. */ #include <sys/types.h> #include <stdio.h> #include <fcntl.h> #include <pwd.h> #include "config.h" #include "shadow.h" #ifndef lint static char _sccsid[] = "@(#)pwunconv.c 2.1 01:23:44 6/20/89"; #endif char buf[BUFSIZ]; char *l64a (); int main () { struct passwd *pw; struct passwd *sgetpwent (); FILE *pwd; FILE *npwd; struct spwd *spwd; int fd; if (! (pwd = fopen (PWDFILE, "r"))) { perror (PWDFILE); return (1); } unlink ("npasswd"); if ((fd = open ("npasswd", O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0 || ! (npwd = fdopen (fd, "w"))) { perror ("npasswd"); return (1); } while (fgets (buf, BUFSIZ, pwd) == buf) { buf[strlen (buf) - 1] = '\0'; /* remove '\n' character */ if (buf[0] == '#') { /* comment line */ (void) fprintf (npwd, "%s\n", buf); continue; } if (! (pw = sgetpwent (buf))) { /* copy bad lines verbatim */ (void) fprintf (npwd, "%s\n", buf); continue; } setspent (); /* rewind shadow file */ if (! (spwd = getspnam (pw->pw_name))) { (void) fprintf (npwd, "%s\n", buf); continue; } pw->pw_passwd = spwd->sp_pwdp; /* * Password aging works differently in the two different systems. * With shadow password files you apparently must have some aging * information. The maxweeks or minweeks may not map exactly. * In pwconv we set max == 10000, which is about 30 years. Here * we have to undo that kludge. So, if maxdays == 10000, no aging * information is put into the new file. Otherwise, the days are * converted to weeks and so on. */ if (spwd->sp_max > (63*7) && spwd->sp_max < 10000) spwd->sp_max = (63*7); /* 10000 is infinity this week */ if (spwd->sp_min >= 0 && spwd->sp_min <= 63*7 && spwd->sp_max >= 0 && spwd->sp_max <= 63*7) { spwd->sp_max /= 7; /* turn it into weeks */ spwd->sp_min /= 7; spwd->sp_lstchg /= 7; pw->pw_age = l64a ((long) spwd->sp_lstchg * (64L*64L) + spwd->sp_min * (64L) + spwd->sp_max); } else pw->pw_age = (char *) 0; if (pw->pw_age) (void) fprintf (npwd, "%s:%s,%s:%d:%d:%s:%s:%s\n", pw->pw_name, pw->pw_passwd ? pw->pw_passwd:"", pw->pw_age, pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir, pw->pw_shell ? pw->pw_shell:""); else (void) fprintf (npwd, "%s:%s:%d:%d:%s:%s:%s\n", pw->pw_name, pw->pw_passwd ? pw->pw_passwd:"", pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir, pw->pw_shell ? pw->pw_shell:""); } endspent (); if (ferror (npwd)) { perror ("pwunconv"); (void) unlink ("npasswd"); } (void) fclose (npwd); (void) fclose (pwd); return (0); } SHAR_EOF fi if test -f 'failure.c' then echo shar: "will not over-write existing file 'failure.c'" else cat << \SHAR_EOF > 'failure.c' #include <sys/types.h> #include <fcntl.h> #include <time.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "faillog.h" #include "config.h" #ifndef lint static char _sccsid[] = "@(#)failure.c 2.1 01:23:16 6/20/89"; #endif #ifdef FAILLOG #define DAY (24L*3600L) #define YEAR (365L*DAY) #define NOW (time ((time_t *) 0)) extern struct tm *localtime (); extern char *asctime (); extern void failprint (); /* * failure - make failure entry */ void failure (uid, tty, faillog) int uid; char *tty; struct faillog *faillog; { int fd; if ((fd = open (FAILFILE, O_RDWR)) < 0) return; lseek (fd, (off_t) (sizeof *faillog) * uid, 0); if (read (fd, (char *) faillog, sizeof *faillog) != sizeof *faillog) #ifndef BSD memset ((void *) faillog, '\0', sizeof *faillog); #else bzero ((char *) faillog, sizeof *faillog); #endif if (faillog->fail_max == 0 || faillog->fail_cnt < faillog->fail_max) faillog->fail_cnt++; strncpy (faillog->fail_line, tty, sizeof faillog->fail_line); faillog->fail_time = time ((time_t *) 0); lseek (fd, (off_t) (sizeof *faillog) * uid, 0); write (fd, (char *) faillog, sizeof *faillog); close (fd); } /* * failcheck - check for failures > allowable * * failcheck() is called AFTER the password has been validated. */ int failcheck (uid, faillog, failed) int uid; struct faillog *faillog; { int fd; int okay = 1; struct faillog fail; if ((fd = open (FAILFILE, O_RDWR)) < 0) return (1); lseek (fd, (off_t) (sizeof *faillog) * uid, 0); if (read (fd, (char *) faillog, sizeof *faillog) == sizeof *faillog) { if (faillog->fail_max != 0 && faillog->fail_cnt >= faillog->fail_max) okay = 0; } if (!failed && okay) { fail = *faillog; fail.fail_cnt = 0; lseek (fd, (off_t) sizeof fail * uid, 0); write (fd, (char *) &fail, sizeof fail); } close (fd); return (okay); } /* * failprint - print line of failure information */ void failprint (uid, fail) struct faillog *fail; { int fd; struct tm *tp; char *lasttime; if (fail->fail_cnt == 0) return; tp = localtime (&fail->fail_time); lasttime = asctime (tp); lasttime[24] = '\0'; if (NOW - fail->fail_time < YEAR) lasttime[19] = '\0'; if (NOW - fail->fail_time < DAY) lasttime = lasttime + 11; if (*lasttime == ' ') lasttime++; printf ("%d %s since last login. Last was %s on %s.\n", fail->fail_cnt, fail->fail_cnt > 1 ? "failures":"failure", lasttime, fail->fail_line); } #endif SHAR_EOF fi if test -f 'faillog.h' then echo shar: "will not over-write existing file 'faillog.h'" else cat << \SHAR_EOF > 'faillog.h' /* * faillog.h - login failure logging file format * * @(#)faillog.h 2.1 01:23:15 6/20/89 * * The login failure file is maintained by login(1) and fail(1L) * Each record in the file represents a separate UID and the file * is indexed in that fashion. */ #define FAILFILE "/usr/adm/faillog" struct faillog { short fail_cnt; /* failures since last success */ short fail_max; /* failures before turning account off */ char fail_line[12]; /* last failure occured here */ time_t fail_time; /* last failure occured then */ }; SHAR_EOF fi if test -f 'faillog.c' then echo shar: "will not over-write existing file 'faillog.c'" else cat << \SHAR_EOF > 'faillog.c' #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <pwd.h> #include <time.h> #ifndef BSD #include <string.h> #include <memory.h> #else #include <strings.h> #define strchr index #define strrchr rindex #endif #include "config.h" #include "faillog.h" #ifndef lint static char _sccsid[] = "@(#)faillog.c 2.1 01:23:12 6/20/89"; #endif FILE *fail; /* failure file stream */ off_t user; /* one single user, specified on command line */ int days; /* number of days to consider for print command */ time_t seconds; /* that number of days in seconds */ int max; /* maximum failure count for fail_max */ int mflg; /* set fail_max for a given user */ int rflg; /* reset fail_cnt for user or all user's */ int uflg; /* set if user is a valid user id */ int tflg; /* print is restricted to most recent days */ struct faillog faillog; /* scratch structure to play with ... */ struct stat statbuf; /* fstat buffer for file size */ extern int optind; extern char *optarg; extern char *asctime (); extern struct passwd *getpwuid (); extern struct passwd *getpwnam (); extern struct passwd *getpwent (); extern struct tm *localtime (); #define DAY (24L*3600L) #define NOW (time ((time_t *) 0)) main (argc, argv) int argc; char **argv; { char *mode; int uid = 0; int c; struct passwd *pwent; if (getuid () == 0) /* only root can update anything */ mode = "r+"; else /* all others can only look */ mode = "r"; if ((fail = fopen (FAILFILE, mode)) == (FILE *) 0) { perror (FAILFILE); exit (1); } while ((c = getopt (argc, argv, "m:pru:t:")) != EOF) { switch (c) { case 'm': max = atoi (optarg); setmax (); break; case 'p': print (); break; case 'r': reset (); break; case 'u': pwent = getpwnam (optarg); if (! pwent) { fprintf (stderr, "Unknown User: %s\n", optarg); exit (1); } uflg++; user = pwent->pw_uid; break; case 't': days = atoi (optarg); seconds = days * DAY; tflg++; break; } } fclose (fail); exit (0); } print () { int uid; off_t offset; if (uflg) { offset = user * sizeof faillog; fstat (fileno (fail), &statbuf); if (offset >= statbuf.st_size) return; fseek (fail, (off_t) user * sizeof faillog, 0); if (fread ((char *) &faillog, sizeof faillog, 1, fail) == 1) print_one (&faillog, user); else perror (FAILFILE); } else { for (uid = 0; fread ((char *) &faillog, sizeof faillog, 1, fail) == 1; uid++) { if (faillog.fail_cnt == 0) continue; if (tflg && NOW - faillog.fail_time > seconds) continue; print_one (&faillog, uid); } } } print_one (faillog, uid) struct faillog *faillog; { static int once; char *cp; struct tm *tm; struct passwd *pwent; if (! once) { printf ("Username Failures Maximum Latest\n"); once++; } pwent = getpwuid (uid); tm = localtime (&faillog->fail_time); cp = asctime (tm); cp[24] = '\0'; if (pwent) { printf ("%-16s %4d %4d", pwent->pw_name, faillog->fail_cnt, faillog->fail_max); if (faillog->fail_time) printf (" %s on %s\n", cp, faillog->fail_line); else putchar ('\n'); } } reset () { int uid = 0; if (uflg) reset_one (user); else for (uid = 0;reset_one (uid);uid++) ; } reset_one (uid) int uid; { off_t offset; offset = uid * sizeof faillog; fstat (fileno (fail), &statbuf); if (offset >= statbuf.st_size) return (0); if (fseek (fail, offset, 0) != 0) { perror (FAILFILE); return (0); } if (fread ((char *) &faillog, sizeof faillog, 1, fail) != 1) { if (! feof (fail)) perror (FAILFILE); return (0); } if (faillog.fail_cnt == 0) return (1); /* don't fill in no holes ... */ faillog.fail_cnt = 0; if (fseek (fail, offset, 0) == 0 && fwrite ((char *) &faillog, sizeof faillog, 1, fail) == 1) { fflush (fail); return (1); } else { perror (FAILFILE); } return (0); } setmax () { int uid = 0; struct passwd *pwent; if (uflg) { setmax_one (user); } else { setpwent (); while (pwent = getpwent ()) setmax_one (pwent->pw_uid); } } setmax_one (uid) int uid; { off_t offset; offset = uid * sizeof faillog; if (fseek (fail, offset, 0) != 0) { perror (FAILFILE); return; } if (fread ((char *) &faillog, sizeof faillog, 1, fail) != 1) { if (! feof (fail)) perror (FAILFILE); } else { #ifndef BSD memset ((char *) &faillog, '\0', sizeof faillog); #else bzero ((char *) &faillog, sizeof faillog); #endif } faillog.fail_max = max; if (fseek (fail, offset, 0) == 0 && fwrite ((char *) &faillog, sizeof faillog, 1, fail) == 1) fflush (fail); else perror (FAILFILE); } SHAR_EOF fi exit 0 # End of shell archive -- John F. Haugh II +-Button of the Week Club:------------- VoiceNet: (512) 832-8832 Data: -8835 | "AIX is a three letter word, InterNet: jfh@rpp386.Cactus.Org | and it's BLUE." UucpNet : <backbone>!bigtex!rpp386!jfh +--------------------------------------
jfh@rpp386.Dallas.TX.US (John F. Haugh II) (06/20/89)
X-Archive-Name: shadow2/part3 Part 3 of second USENET release -- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # login.1 # passwd.1 # passwd.4 # shadow.3 # shadow.4 # su.1 # sulogin.8 # pwconv.8 # pwunconv.8 # faillog.8 # faillog.4 # This archive created: Tue Jun 20 01:28:09 1989 # By: John F. Haugh II (River Parishes Programming, Plano TX) export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'login.1' then echo shar: "will not over-write existing file 'login.1'" else cat << \SHAR_EOF > 'login.1' .TH LOGIN 1 .SH NAME login \- Begin session on the system .SH SYNOPSIS .B login [ username [ environmental-variables ] ] .SH DESCRIPTION .I login is used to establish a new session with the system. It is normally invoked automatically by responding to the .B login: prompt on the user\'s terminal. .I login may be special to the shell and may not be invoked as a sub-process. Typically, .I login is treated by the shell as \fBexec login\fR which causes the user to exit from the current shell. Attempting to execute \fIlogin\fR from any shell but the login shell will produce an error message. .PP When invoked from the \fBlogin:\fR prompt, the user may enter environmental variables after the username. These variables are entered in the form \fBNAME=VALUE\fR. Not all variables may be set in the fashion, notably \fBPATH\fR, \fBHOME\fR and \fBSHELL\fR. Additionally, \fBIFS\fR may be inhibited if the user\'s login shell is \fB/bin/sh\fR. .PP The user is then prompted for a password, where appropriate. Echoing is disabled to prevent revealing the password. Only a small number of password failures are permitted before \fIlogin\fR exits and the communications link is severed. .PP If password aging has been enabled for your account, you may be prompted for a new password before proceeding. You will be forced to provide your old password and the new password before continuing. Please refer to \fIpasswd(1)\fR for more information. .PP After a successful login, you will be informed of any system messages and the presence of mail. You may turn off the printing of the system message file, \fI/etc/motd\fR, by creating a zero-length file \fI.hushlogin\fR in your login directory. The mail message will be one of "\fIYou have new mail.\fR", "\fIYou have mail.\fR", or "\fINo Mail.\fR" according to the condition of your mailbox. .PP Your user and group ID will be set according to their values in the \fI/etc/passwd\fR file. The value for \fB$HOME\fR, \fB$SHELL\fR, \fB$PATH\fR, \fB$LOGNAME\fR, and \fB$MAIL\fR are set according to the appropriate fields in the password entry. Ulimit, umask and nice values may also be set according to entries in the GECOS field. .PP On some installations, the environmental variable \fB$TERM\fR will be initialize to the terminal type on your tty line, as specified in \fI/etc/ttytype\fR. .PP An initialization script for your command interpreter may also be executed. Please see the appropriate manual section for more information on this function. .SH CAVEATS .PP This version of \fIlogin\fR has many compilation options, only some of which may be in use at any particular site. .SH Files /etc/utmp \- list of current login sessions .br /etc/wtmp \- list of previous login sessions .br /etc/passwd \- user account information .br /etc/shadow \- encrypted passwords and age information .br /etc/motd \- system message file .br /etc/ttytype \- list of terminal types .br $HOME/.profile \- initialization script for default shell .br $HOME/.hushlogin \- suppress printing of system messages .br .SH See Also .PP getty(1M), mail(1), passwd(1), sh(1), su(1), d_passwd(4), passwd(4) SHAR_EOF fi if test -f 'passwd.1' then echo shar: "will not over-write existing file 'passwd.1'" else cat << \SHAR_EOF > 'passwd.1' .TH PASSWD 1 .SH NAME passwd \- change user password .SH SYNOPSIS \fBpasswd\fR [ \fB-f\fR ] [ \fIname\fR ] .SH DESCRIPTION \fIpasswd\f changes passwords for user accounts. A normal user may only change the password for their own account, the super user may change the password for any account. .PP The user is first prompted for their old password, if one is present. This password is then encrypted and compared against the stored password. The user has only one chance to enter the correct password. The super user is permitted to bypass this step so that forgotten passwords may be changed. .PP After the password has been entered password aging information is checked to see if the user is permitted to change their password at this time. If not, \fIpasswd\fR refuses to change the password and exits. .PP The user is then prompted for a replacement password. This password is tested for complexity. As a general guideline, passwords should consist of 6 to 8 characters including one or more from each of following sets: .IP "" .5i Lower case alphabetics .IP "" .5i Upper case alphabetics .IP "" .5i Digits 0 thru 9 .IP "" .5i Punctuation marks .PP Care must be taken not to include the system default erase or kill characters. \fIpasswd\fR will reject any password which is not suitably complex. .PP If the password is accepted, \fIpasswd\fR will prompt again and compare the second entry against the first. Both entries are require to match in order for the password to be changed. .SH Hints The security of a password depends upon the strength of the encryption algorithm and the size of the key space. The \fB\s-2UNIX\s+2\fR System encryption method is based on the NBS DES algorithm and is very secure. .PP Compromises in password security normally result from careless password selection or handling. For this reason, you should select a password which does not appear in a dictionary or which must be written down. The password should also not be a proper name, your license number, birth date, or street address. Any of these may be used as guesses to violate system security. .PP Your password must easily remembered so that you will not be forced to write it on a piece of paper. This can be accomplished by appending two small words together and separating each with a special character or digit. For example, Pass%word. .PP Other methods of construction involve selecting an easily remembered phrase from literature and selecting the first or last letter from each. An example of this is .IP "" .5i Ask not for whom the bell tolls. .PP which produces .IP "" .5i An4wtbt. .PP You may be reasonably sure few crackers will have included this in their dictionary. .SH CAVEATS Not all options may be supported. Password complexity checking may vary from site to site. The user is urged to select as complex a password as they feel comfortable with. A \fB-f\fR option exists to permit the superuser to override any password complexity testing, normal users must create passwords which pass the complexity test. .SH Files /etc/passwd \- user account information .br /etc/shadow \- encrypted user passwords .SH See Also passwd(3), passwd(4) SHAR_EOF fi if test -f 'passwd.4' then echo shar: "will not over-write existing file 'passwd.4'" else cat << \SHAR_EOF > 'passwd.4' .TH PASSWD 4 .SH NAME passwd \- The password file .SH DESCRIPTION .I passwd contains various pieces of information for each user account. Included is .IP "" .5i Login name .IP "" .5i Optional encrypted password .IP "" .5i Numerical user ID .IP "" .5i Numerical group ID .IP "" .5i User name or comment field .IP "" .5i User home directory .IP "" .5i User command interpreter .PP The password field may not be filled if shadow passwords have been enabled. If shadow passwords are being used, the encrypted password will be found in \fB/etc/shadow\fR. The encryped password consists of 13 characters from the 64 character alphabet a thru z, A thru Z, 0 thru 9, \. and /. Refer to \fIcrypt(3)\fR for details on how this string is interpreted. .PP An optional password age string may follow the encrypted password, separated by a comma, from the same alphabet as the password itself. The first character gives the number of weeks during which the password is valid. The second character gives the number of weeks which must pass before the user is permitted to change the password. The last two characters give the week since Jan 1970 when the password was last changed. When the number of weeks during which the password is valid have passed, the user will be required to provide a new password. .PP The comment field is used by various system utilities, such as \fIfinger(1)\fR. Three additional values may be present in the comment field. They are .IP "" .5i pri= \- set initial value of nice .IP "" .5i umask= \- set initial value of umask .IP "" .5i ulimit= \- set initial value of ulimit .PP These fields are separated from each other and from any other comment field by a comma. .PP The home directory field provides the name of the initial working directory. \fILogin\fR uses this information to set the value of the \fBHOME\fR environmental variable. .PP The command interpreter field provides the name of the user's command language interpreter, or the name of the initial program to execute. \fILogin\fR uses this information to set the value of the \fBSHELL\fR environmental variable. If this field is empty, it defaults to the value \fB/bin/sh\fR. .SH Files /etc/passwd \- user account information .SH See Also login(1), passwd(1), su(1), sulogin(1M), shadow(4), pwconv(8), pwunconv(8) SHAR_EOF fi if test -f 'shadow.3' then echo shar: "will not over-write existing file 'shadow.3'" else cat << \SHAR_EOF > 'shadow.3' .TH SHADOW 3 .SH NAME shadow \- encrypted password file routines .SH Syntax .IP "" .5i #include <shadow.h> .IP "" .5i struct spwd *getspent(); .br struct spwd *getspnam(char * name); .br void setspent(); .br void endspent(); .br struct spwd *fgetspent(FILE *fp); .br int putspent(struct spwd *p,FILE *fp); .SH DESCRIPTION .I shadow manipulates the contents of the shadow password file, \fB/etc/shadow\fR. The structure in the \fI#include\fR file is .IP "" .5i struct spwd { .br char *sp_namp; /* user login name */ .br char *sp_pwdp; /* encrypted password */ .br long sp_lstchg; /* last password change */ .br int sp_max; /* days before change required */ .br int sp_min; /* days until change allowed. */ .br } .PP The meanings of each field are .IP "" .5i sp_namp \- pointer to null-terminated user name. .IP "" .5i sp_pwdp \- pointer to null-terminated password. .IP "" .5i sp_lstchg \- days since Jan 1, 1970 password was last changed. .IP "" .5i sp_max \- days after which password must be changed .IP "" .5i sp_min \- days before which password may not be changed. .SH Description \fIgetspent\fR, \fIgetspname\fR, and \fIfgetspent\fR each return a pointer to a \fBstruct spent\fR. \fIgetspent\fR returns the next entry from the file, and \fIfgetspent\fR returns the next entry from the given stream, which is assumed to be a file of the proper format. \fIgetspnam\fR searches from the current position in the file for an entry matching \fIname\fR. .PP \fIsetspent\fR and \fIendspent\fR may be used to begin and end, respectively, access to the shadow password file. .SH Diagnostics Routines return NULL if no more entries are available or if an error occurs during processing. .SH Caveats These routines may only be used by the super user as access to the shadow password file is restricted. .SH Files /etc/shadow \- encrypted user passwords .SH See Also getpwent(3), shadow(4) SHAR_EOF fi if test -f 'shadow.4' then echo shar: "will not over-write existing file 'shadow.4'" else cat << \SHAR_EOF > 'shadow.4' .TH SHADOW 4 .SH NAME shadow \- encrypted password file .SH DESCRIPTION .I shadow contains the encrypted password information for user's accounts and optional the password aging information. Included is .IP "" .5i Login name .IP "" .5i Encrypted password .IP "" .5i Date password last changed .IP "" .5i Days before password may be changed .IP "" .5i Days after which password must be changed .PP The password field must be filled. The encryped password consists of 13 characters from the 64 character alphabet a thru z, A thru Z, 0 thru 9, \. and /. Refer to \fIcrypt(3)\fR for details on how this string is interpreted. .PP The date of the last password change is given as the number of days since Jan 1, 1970. The password may not be changed again until the proper number of days have passed, and must be changed after the maximum number of days. If the minimum number of days required is greater than the maximum number of day allowed, this password may not be changed by the user. .PP This information supercedes any password or password age information present in \fB/etc/passwd\fR. .PP This file must not be readable by regular users if password security is to be maintained. .SH Files /etc/passwd \- user account information .br /etc/shadow \- encrypted user passwords .SH See Also login(1), passwd(1), su(1), sulogin(1M), passwd(4), pwconv(8), pwunconv(8) SHAR_EOF fi if test -f 'su.1' then echo shar: "will not over-write existing file 'su.1'" else cat << \SHAR_EOF > 'su.1' .TH SU 1 .SH NAME su \- Change user ID or become super-user .SH SYNOPSIS .B su [ - ] [ username [ args ] ] .SH DESCRIPTION .I su is used to become another user during a login session. Invoked without a username, \fIsu\fR defaults to becoming the super user. The optional argument \fB\-\fR may be used to provide an environment similiar to what the user would expect had the user logged in directly. .PP Additional arguments may be provided after the username, in which case they are supplied to the user\'s login shell. In particular, an argument of \fB-c\fR will cause the next argument to be treated as a command by most command interpreters. The command will be executed under the shell specified by \fB$SHELL\fR, or if undefined, by the one specified in \fI/etc/passwd\fR. .PP The user will be prompted for a password, if appropriate. Invalid passwords will produce an error message. All attempts, both valid and invalid, are logged to detect abuses of the system. .PP The current environment is passed to the new shell. The value of \fB$PATH\fR is reset to \fB/bin:/usr/bin\fR for normal users, or \fB/bin:/usr/bin:/etc\fR for the super user. .SH CAVEATS .PP This version of \fIsu\fR has many compilation options, only some of which may be in use at any particular site. .SH Files /etc/passwd \- user account information .br /etc/shadow \- encrypted passwords and age information .br $HOME/.profile \- initialization script for default shell .SH See Also login(1), sh(1) SHAR_EOF fi if test -f 'sulogin.8' then echo shar: "will not over-write existing file 'sulogin.8'" else cat << \SHAR_EOF > 'sulogin.8' .TH SULOGIN 8 .SH NAME sulogin \- Single-user login .SH DESCRIPTION .I sulogin is invoked by \fB/etc/init\fR prior to allowing the user access to the system when in single user mode. This feature may only be available on certain systems where \fIinit\fR has been modified accordingly, or where the \fB/etc/inittab\fR has an entry for a single user login. .PP The user is prompted .IP "" .5i Type control-d for normal startup, .br (or give root password for system maintenance): .PP If the user enters the correct root password, a login session is initiated. When \fBEOF\fR is pressed instead, the system enters multi-user mode. .PP After the user exits the single-user shell, or presses \fBEOF\fR, the system begins the initialization process required to enter multi-user mode. .SH CAVEATS .PP This command can only be used if \fIinit\fR has been modified to call \fB/etc/sulogin\fR instead of \fB/bin/sh\fR, or if the user has set the \fIinittab\fR to support a single user login. .PP As complete an environment as possible is created. However, various devices may be unmounted or uninitialized and many of the user commands may be unavailable or nonfunctional as a result. .SH Files /etc/passwd \- user account information .br /etc/shadow \- encrypted passwords and age information .br /.profile \- initialization script for single user shell .SH See Also login(1), init(1M), sh(1) SHAR_EOF fi if test -f 'pwconv.8' then echo shar: "will not over-write existing file 'pwconv.8'" else cat << \SHAR_EOF > 'pwconv.8' .TH PWCONV 8 .SH NAME pwconv \- convert and update shadow password files .SH SYNOPSIS /etc/pwconv .SH DESCRIPTION \fIPwconv\fR copies the old password file information to a new shadow password file, merging entries from an optional existing shadow file. The new password file is left in \fBnpasswd\fR, the new shadow file is left in \fBnshadow\fR. Both of these are files are created with modes which only permit read access to the owner. Existing shadow entries are copied as is. New entries are created with passwords which expire in 10000 days, with a last changed date of today, unless password aging information was already present. Entries with blank passwords are not copied to the shadow file at all. .SH Files /etc/passwd \- old encrypted passwords and password aging .br /etc/shadow \- previously converted shadow password file .br ./npasswd \- new password file .br ./nshadow \- new shadow password file .SH See Also passwd(1), passwd(4), shadow(4), pwunconv(8) SHAR_EOF fi if test -f 'pwunconv.8' then echo shar: "will not over-write existing file 'pwunconv.8'" else cat << \SHAR_EOF > 'pwunconv.8' .TH PWUNCONV 8 .SH NAME pwunconv \- restore old password file from shadow password file .SH SYNOPSIS /etc/pwunconv .SH DESCRIPTION \fIPwunconv\fR copies the password file information from the shadow password file, merging entries from an optional existing shadow file. The new password file is left in \fBnpasswd\fR. This file is created with modes which allow read access for the owner only. There is no new shadow file. Password aging information is translated where possible. .SH Files /etc/passwd \- old encrypted passwords and password aging .br /etc/shadow \- previously converted shadow password file .br ./npasswd \- new password file .SH See Also passwd(1), passwd(4), shadow(4), pwconv(8) SHAR_EOF fi if test -f 'faillog.8' then echo shar: "will not over-write existing file 'faillog.8'" else cat << \SHAR_EOF > 'faillog.8' .TH FAILLOG 8 .SH NAME faillog \- examine faillog and set login failure limits .SH SYNOPSIS /etc/faillog [ -u uid ] [ -t days ] [ -m max ] [ -pr ] .SH DESCRIPTION \fIPlastlog\fR formats the contents of the failure log, \fI/usr/adm/faillog\fR, and maintains failure counts and limits. The order of the arguments to \fIfaillog\fR is significant. Each argument is processed immediately in the order given. .PP The \fB-p\fR flag causes failure entries to be printed in UID order. Entering \fB-u login-name\fR flag will cause the failure record for \fBlogin-name\fR only to be printed. Entering \fB-t days\fR will cause only the failures more recent than \fBdays\fR to be printed. The \fB-t\fR flag overrides the use of \fB-u\fR. .PP The \fB-r\fR flag is used to reset the count of login failures. Write access to \fI/usr/adm/faillog\fR is required for this option. Entering \fB-u login-name\fR will cause only the failure count for \fBlogin-name\fR to be reset. .PP The \fB-m\fR flag is used to set the maximum number of login failures before the account is disabled. Write access to \fB/usr/adm/faillog\fR is required for this option. Entering \fB-m max\fR will cause all accounts to be disabled after \fBmax\fR failed logins occur. This may be modified with \fB-u login-name\fR to limit this function to \fBlogin-name\fR only. Selecting a \fBmax\fR value of 0 has the effect of not placing a limit on the number of failed logins. The maximum failure count should always be 0 for \fBroot\fR to prevent a denial of services attack against the system. .PP Options may be combined in virtually any fashion. Each \fB-p\fR, \fB-r\fR, and \fB-m\fR option will cause immediate execution using any \fB-u\fR or \fB-t\fR modifier. .SH Files /usr/adm/faillog \- failure logging file .SH See Also login(1), faillog(4) SHAR_EOF fi if test -f 'faillog.4' then echo shar: "will not over-write existing file 'faillog.4'" else cat << \SHAR_EOF > 'faillog.4' .TH FAILLOG 4 .SH NAME faillog \- Login failure logging file .SH DESCRIPTION .I faillog maintains a count of login failures and the limits for each account. The file is fixed length record, indexed by numerical UID. Each record contains the count of login failures since the last successful login; the maximum number of failures before the account is disabled; the line the last login failure occured on; and the date the last login failure occured. .PP The structure of the file is .DS struct faillog { short fail_cnt; short fail_max; char fail_line[12]; time_t fail_time; }; .DE .SH Files /usr/adm/faillog \- login failure log .SH See Also faillog(8) SHAR_EOF fi exit 0 # End of shell archive -- John F. Haugh II +-Button of the Week Club:------------- VoiceNet: (512) 832-8832 Data: -8835 | "AIX is a three letter word, InterNet: jfh@rpp386.Cactus.Org | and it's BLUE." UucpNet : <backbone>!bigtex!rpp386!jfh +--------------------------------------