v06i023: shadow password routines, part 2
Brandon S. Allbery - comp.sources.misc
allbery at uunet.UU.NET
Mon Jan 30 07:12:45 AEST 1989
Posting-number: Volume 6, Issue 23
Submitted-by: jfh at hal.CWRU.Edu@convex.UUCP (John F. Haugh II)
Archive-name: shadow-2.pt2
[No Subject: line -- biggest violation in the rulebook. How am I supposed
to know this is multipart ahead of schedule? ++bsa]
#! /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: dlm at 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: <2189 at cuuxb.ATT.COM>
Date: 11 Nov 88 21:33:37 GMT
References: <16722 at agate.BERKELEY.EDU> <2178 at cuuxb.ATT.COM> <16768 at agate.BERKELEY.EDU> <17828 at glacier.STANFORD.EDU> <2182 at cuuxb.ATT.COM> <8
Reply-To: dlm at cuuxb.UUCP (Dennis L. Mumaugh)
Organization: ATT Data Systems Group, Lisle, Ill.
Lines: 60
In article <8861 at smoke.BRL.MIL> gwyn at 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!dlm at 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
More information about the Comp.sources.misc
mailing list