#! /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: # log.c # mail.c # shadow.h # sulog.c # Makefile # entry.c # obscure.c # setup.c # sub.c # config.h # shadow.info # pmain.c # sulogin.c # dialup.h # This archive created: Sun Jan 22 22:24:51 1989 # By: John F. Haugh II (River Parishes Programming, Dallas TX) export PATH; PATH=/bin:/usr/bin:$PATH 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> #include <string.h> #include "config.h" extern struct utmp utent; extern struct passwd pwent; extern struct lastlog lastlog; extern char **environ; long lseek (); time_t time (); #ifdef LASTLOG #include "lastlog.h" void log () { int fd; long 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) return; if (read (fd, (char *) &lastlog, sizeof lastlog) != sizeof lastlog) memset ((char *) &lastlog, sizeof lastlog, 0); 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> #include <string.h> #include "config.h" extern char mail[]; #ifdef MAILCHECK void mailcheck () { struct stat statbuf; char *mailbox; 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 password security file structure. */ struct spwd { char *sp_namp; /* login name */ char *sp_pwdp; /* encrypted password */ long sp_lstchg; /* date of last change */ int sp_max; /* maximum number of days between changes */ int 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> #include <string.h> #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' SHELL = /bin/sh # Flags for SCO Xenix/386 CFLAGS = -O -M3 -g -DFGETPWENT LIBS = -lcrypt LDFLAGS = -M3 -g LTFLAGS = # Flags for normal machines # CFLAGS = -O -g # LIBS = # LDFLAGS = -g 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 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.o dialchk.o 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 FILES1 = log.c mail.c shadow.h sulog.c Makefile entry.c obscure.c \ setup.c sub.c config.h shadow.info pmain.c sulogin.c dialup.h 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 DOCS = login.1 passwd.1 passwd.4 shadow.3 shadow.4 su.1 sulogin.8 pwconv.8 \ pwunconv.8 all: su login pwconv pwunconv passwd sulogin lint: su.L login.L pwconv.L pwunconv.L passwd.L sulogin.L 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 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 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 clean: -rm -f *.o a.out core npasswd nshadow clobber: clean -rm -f su login passwd pwconv pwunconv sulogin *.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> #include <string.h> #include "config.h" #ifdef SHADOWPWD #include "shadow.h" #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; endspent (); return; } 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); 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> #include "config.h" /* * 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 */ char mono[32]; /* a monocase version of pass */ int obscure () { int i; if (orig[0] == '\0') return (1); if (strlen (pass) < 6) { /* 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> #include <string.h> #include "config.h" extern char home[]; extern char prog[]; extern char name[]; extern char mail[]; #ifndef SU extern struct utmp utent; #endif #ifdef QUOTAS long strtol (); long ulimit (); #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; } if (strncmp (cp, "ulimit=", 6) == 0) { l = strtol (cp + 6, (char **) 0, 10); (void) ulimit (2, l); continue; } if (strncmp (cp, "umask=", 5) == 0) { i = strtol (cp + 5, (char **) 0, 8) & 0777; (void) umask (i); continue; } } #endif if (setgid (info->pw_gid) == -1) { puts ("Bad group id"); exit (errno); } if (setuid (info->pw_uid) == -1) { 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); #ifndef SU (void) strcat (strcpy (logname, "LOGNAME="), name); addenv (logname); #endif (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> #include <string.h> 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 (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[1] == '\0') pwent.pw_shell = "/bin/sh"; else pwent.pw_shell++; #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. */ /* * Define DIALUP to use dialup password files */ #define DIALUP /* * Define SHADOWPWD to use shadow [ unreadable ] password file */ #define SHADOWPWD /* * 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 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 */ #define TZ "TZ=CST6CDT" /* * Define the default PATH and SUPATH here. PATH is for non-privileged * users, SUPATH is for root. */ #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 single terminal */ #define CONSOLE "tty01" /* * 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 MOTD is #define'd. */ #define HUSHLOGIN /* * Define LASTLOG if you want a record made of logins in /usr/adm/lastlog. */ #define LASTLOG /* * Define TTYPERM to be the initial terminal permissions. Defining * as 0600 will not allow messages, 0622 will. */ #define TTYPERM 0600 /* * 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" SHAR_EOF fi if test -f 'shadow.info' then echo shar: "will not over-write existing file 'shadow.info'" else cat << \SHAR_EOF > 'shadow.info' >From vector!killer!osu-cis!att!cuuxb!dlm Sun Nov 13 08:11:08 CST 1988 Article 5025 of comp.unix.wizards: Path: rpp386!vector!killer!osu-cis!att!cuuxb!dlm >From: d...@cuuxb.ATT.COM (Dennis L. Mumaugh) Newsgroups: comp.unix.wizards Subject: /etc/shadow Summary: See release notes for SVR3.2 Keywords: shadow password Message-ID: <2...@cuuxb.ATT.COM> Date: 11 Nov 88 21:33:37 GMT References: <16...@agate.BERKELEY.EDU> <2...@cuuxb.ATT.COM> <16...@agate.BERKELEY.EDU> <17...@glacier.STANFORD.EDU> <2...@cuuxb.ATT.COM> <8 Reply-To: d...@cuuxb.UUCP (Dennis L. Mumaugh) Organization: ATT Data Systems Group, Lisle, Ill. Lines: 60 In article <8...@smoke.BRL.MIL> g...@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes: >It would be a great service to the community if specifications for >this feature were posted or at least sent to developers who want >to enable a similar feature on their (typically BSD-based) systems. >For example, what is the shadow file called, what is its format, >what sort of stuff is left in the password field in /etc/passwd, >what facilities are there to validate a password against the >shadow encrypted password file? The documentation is scattered in the Release Notes for System V Release 3.2. Of course they don't have a page shadow(4) but: The file is /etc/shadow and is owned by root and mode 400. It contains one line per login. Fields are separated by colons: username \- users login name password \- A 13 character encrypted password or a lock string to indicater the login is not accessible lastchanged \- number of days since January 1, 1970 that the password has been modified min \- the number of days required between password changes max \- the maximum number of days the password is valid. Routines to work with /etc/shadow: #include <shadow.h> struct spwd *getspent(); struct spwd *getspnam(char * name); void setspent(); void endspent(); struct spwd *fgetspent(FILE *fp); int putspent(struct spwd *p,FILE *fp); Programs allied with this are pwconv \- install and/or update /etc/shadow with information from /etc/passwd pwunconv \- restore /etc/password from /etc/shadown Programs like login, su and passwd work with either /etc/passwd ONLY or with the added /etc/shadow. If there is no entry in /etc/shadow we accept the /etc/passwd as gospel [in case someone forgot to run /usr/lib/pwconv after adding a user.] Also /usr/include/shadow.h: struct spwd { char *sp_namp; /* users login name */ char *sp_pwdp; /* encrypted password */ long sp_lstchg; /* number of days since January 1, 1970 that the password has been modified */ int sp_max; /* the number of days required between password changes */ int sp_min; /* the maximum number of days the password is valid. */ } #define SHADOW "/etc/shadow" ATT doesn't provide any of the functions or the header file as part of its product. It is in the source but not the binary. Thus developers who need the routines must contact their ATT person [not me!] to obtain the shadow password security library -- =Dennis L. Mumaugh Lisle, IL ...!{att,lll-crg}!cuuxb!dlm OR cuuxb!...@arpa.att.com 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> #include <string.h> #include "config.h" #include "lastlog.h" #include "shadow.h" 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; { 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 argc--; argv++; /* shift ... */ 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 */ if (! amroot) force = 0; 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); if (!force && ! obscure ()) { #ifdef OBSCURE puts ("Password not changed."); exit (1); #else 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 = crypt (pass, l64a (salttime)); /* * 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 (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; (void) putspent (spwd, npwd); } 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; } if (link (NSHADOW, SHADOW) || unlink (NSHADOW)) { (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", 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 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; } if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE)) { 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*/ } 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> #include "config.h" #include "lastlog.h" char name[BUFSIZ]; char pass[BUFSIZ]; char home[BUFSIZ]; char prog[BUFSIZ]; char mail[BUFSIZ]; struct passwd pwent; struct utmp utent; struct lastlog lastlog; #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 addenv (TZ); /* set the default $TZ, if one */ #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 and let INIT go to runlevel 2. */ /* 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/ */ strcpy (utent.ut_line, cp); /* needed for tty perms (setup) */ 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. */ 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 exit 0 # End of shell archive