Update passwords from a file (Part 1 of 2)
John F. Haugh II
jfh at rpp386.cactus.org
Sat Nov 17 15:47:40 AEST 1990
#! /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
# Makefile
# shadow.h
# config.h.std
# grent.c
# chpasswd.c
# shadowio.c
# pwio.c
# This archive created: Fri Nov 16 22:38:02 1990
# By: John F. Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'README'" '(2813 characters)'
if test -f 'README'
then
echo shar: "will not over-write existing file 'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XThis little code drop contains two different utilities. The first
Xoutlines how to modify password files in "batch" mode. The second
Xis a utility for creating users in "batch" mode.
X
XThe first utility, "chpasswd", reads its standard input for colon
Xseparated pairs of user names and passwords. The password for the
Xnamed user is set to the cleartext which follows. A random salt
Xis generated and used as input to the crypt() subroutine.
X
XThe second utility, "newusers", reads its standard input for
Xrecords looking like entries in a password file. The entries in
Xthis file are used to update the password file entries of existing
Xusers, or to create new entries if the user does not exist. The
Xexpected values for the fields are -
X
X name - a normal user name
X password - the cleartext of the user's password. it
X will be encrypted with a random salt. it
X can't be blank just yet (or a value making
X the account expired. give me some time).
X uid - blank, or an integer. the uid is figured as
X being the next highest uid. this may have
X some problems.
X gid - blank, or a group name. if blank, the gid will
X be either the same as the uid, or the next
X available gid. if a name, the user will be
X added as a member. this isn't that well
X thought out just yet ...
X gecos - the new gecos field, or blank.
X dir - the new home directory (which will be created
X if it is non-blank and non-existent), or blank.
X shell - the new login shell, or blank.
X
XEntries will be added to /etc/group as required, and if requested,
Xentries to /etc/shadow will be updated as well. See the code if
Xyou have any questions. Documentation will follow later. Oh -
Xthe /etc/shadow file is in SVR4 format. Sorry, but that is the
Xdirection this work is going. If you want SVR3.2 format, run
Xwithout shadowing turned on, then use the pwconv utility to update
Xthe shadow file. To get vanilla UNIX (no shadows of any kind)
Xuse "config.h.std" in place of "config.h"; it has the correct
Xdefines to get you boring, dull, uninteresting password files.
X
XAlong with this you get the first [?] release of a collection of
Xlibrary routines for dealing with password files in a more orderly
Xfashion. There will be more documentation and features in the
Xfuture. The interface that this code presents is intended to
Xremain fixed forever, with only the implmentation changing as
Xneeded.
X
XSteve Simmons presented a talk on this work at the recent LISA
Xconference. If you attended his talk, this is some of the code
Xthat is being worked on.
X
XPlease notice that this code is COPYRIGHTED and that I do have a
Xpolicy of enforcing copyrights. This is to protect the integrity
Xof the work and not to restrict its use or normal distribution.
XIf you have any questions regarding commercial distribution,
Xplease feel free to contact me.
SHAR_EOF
if test 2813 -ne "`wc -c < 'README'`"
then
echo shar: "error transmitting 'README'" '(should have been 2813 characters)'
fi
fi
echo shar: "extracting 'Makefile'" '(568 characters)'
if test -f 'Makefile'
then
echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
XLIBS = -ldbm -lcrypt
XCFLAGS = -g
X
Xchpasswd: chpasswd.o libshadow.a
X cc -o chpasswd -g chpasswd.o libshadow.a $(LIBS)
X
Xnewusers: newusers.o libshadow.a
X cc -o newusers -g newusers.o libshadow.a $(LIBS)
X
Xchpasswd.o: config.h shadow.h
Xencrypt.o: config.h
Xpwent.o: config.h
Xshadow.o: shadow.h
Xshadowio.o: shadow.h
X
Xlibshadow.a: \
X libshadow.a(encrypt.o) \
X libshadow.a(grent.o) \
X libshadow.a(groupio.o) \
X libshadow.a(pwent.o) \
X libshadow.a(pwio.o) \
X libshadow.a(pwpack.o) \
X libshadow.a(rad64.o) \
X libshadow.a(shadow.o) \
X libshadow.a(shadowio.o)
X ranlib libshadow.a
SHAR_EOF
if test 568 -ne "`wc -c < 'Makefile'`"
then
echo shar: "error transmitting 'Makefile'" '(should have been 568 characters)'
fi
fi
echo shar: "extracting 'shadow.h'" '(1223 characters)'
if test -f 'shadow.h'
then
echo shar: "will not over-write existing file 'shadow.h'"
else
sed 's/^X//' << \SHAR_EOF > 'shadow.h'
X/*
X * Copyright 1988, 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X/*
X * This information is not derived from AT&T licensed sources. Posted
X * to the USENET 11/88, and updated 11/90 with information from SVR4.
X *
X * @(#)shadow.h 3.1 10:14:23 11/9/90
X */
X
X/*
X * Shadow password security file structure.
X */
X
Xstruct spwd {
X char *sp_namp; /* login name */
X char *sp_pwdp; /* encrypted password */
X long sp_lstchg; /* date of last change */
X long sp_min; /* minimum number of days between changes */
X long sp_max; /* maximum number of days between changes */
X long sp_warn; /* number of days of warning before password
X expires */
X long sp_inact; /* number of days after password expires
X until the account becomes unusable. */
X long sp_expire; /* days since 1/1/70 until account expires */
X unsigned long sp_flag; /* reserved for future use */
X};
X
X/*
X * Shadow password security file functions.
X */
X
Xstruct spwd *getspent ();
Xstruct spwd *getspnam ();
Xstruct spwd *sgetspent ();
Xvoid setspent ();
Xvoid endspent ();
Xstruct spwd *fgetspent ();
Xint putspent ();
X
X#define SHADOW "/etc/shadow"
SHAR_EOF
if test 1223 -ne "`wc -c < 'shadow.h'`"
then
echo shar: "error transmitting 'shadow.h'" '(should have been 1223 characters)'
fi
fi
echo shar: "extracting 'config.h.std'" '(1449 characters)'
if test -f 'config.h.std'
then
echo shar: "will not over-write existing file 'config.h.std'"
else
sed 's/^X//' << \SHAR_EOF > 'config.h.std'
X/*
X * Copyright 1989, 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X */
X
X/*
X * Configuration file for login. (Hacked on badly ...)
X *
X * @(#)config.h 2.6 08:26:28 8/20/90
X */
X
X/*
X * Define SHADOWPWD to use shadow [ unreadable ] password file
X */
X
X#undef SHADOWPWD
X
X/*
X * Define DOUBLESIZE to use 16 character passwords
X */
X
X#undef DOUBLESIZE
X
X/*
X * Define MAXDAYS to be the default maximum number of days a password
X * is valid for when converting to shadow passwords. Define MINDAYS
X * to be the minimum number of days before a password may be changed.
X * See pwconv.c for more details.
X */
X
X#define MAXDAYS 10000
X#define MINDAYS 0
X
X/*
X * Define WARNAGE to be the number of days notice a user receives
X * of a soon to expire password. Setting this to a value other than
X * -1 will force SVR4-style shadow password entries to be emitted.
X */
X
X#undef WARNAGE 10
X
X/*
X * Pick your version of DBM. Only DBM is presently supported, NDBM will
X * follow. You must also define the GETPWENT macro below.
X */
X
X#define DBM
X
X/*
X * Wierd stuff follows ...
X *
X * The following macros exist solely to override stuff ...
X * You will probably want to change their values to suit your
X * fancy.
X */
X
X#define UMASK 022
X
X#define FGETPWENT /* Define if library does not include FGETPWENT */
X#define GETPWENT /* Define if you want my GETPWENT(3) routines */
SHAR_EOF
if test 1449 -ne "`wc -c < 'config.h.std'`"
then
echo shar: "error transmitting 'config.h.std'" '(should have been 1449 characters)'
fi
fi
echo shar: "extracting 'grent.c'" '(1893 characters)'
if test -f 'grent.c'
then
echo shar: "will not over-write existing file 'grent.c'"
else
sed 's/^X//' << \SHAR_EOF > 'grent.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Non-commercial distribution permitted. You must provide this source
X * code in any distribution. This notice must remain intact.
X */
X
X#include <stdio.h>
X#include <grp.h>
X#include <string.h>
X#include "config.h"
X#ifdef DBM
X#include <dbm.h>
X#endif
X
X#ifndef lint
Xstatic char _sccsid[] = "@(#)grent.c 3.1 08:52:58 11/15/90";
X#endif
X
X#define NFIELDS 4
X#define MAXMEM 1024
X
Xstatic char grpbuf[4*BUFSIZ];
Xstatic char *grpfields[NFIELDS];
Xstatic char *members[MAXMEM+1];
X
Xstatic char **
Xlist (s)
Xchar *s;
X{
X int nmembers = 0;
X
X while (*s) {
X members[nmembers++] = s;
X if (s = strchr (s, ','))
X *s++ = '\0';
X }
X members[nmembers] = (char *) 0;
X return members;
X}
X
Xstruct group *sgetgrent (buf)
Xchar *buf;
X{
X int i;
X char *cp;
X static struct group grent;
X
X strncpy (grpbuf, buf, sizeof grpbuf);
X grpbuf[sizeof grpbuf - 1] = '\0';
X if (cp = strrchr (grpbuf, '\n'))
X *cp = '\0';
X
X for (cp = grpbuf, i = 0;i < NFIELDS && cp;i++) {
X grpfields[i] = cp;
X if (cp = strchr (cp, ':'))
X *cp++ = 0;
X }
X if (i < (NFIELDS-1) || *grpfields[2] == '\0')
X return ((struct group *) 0);
X
X grent.gr_name = grpfields[0];
X grent.gr_passwd = grpfields[1];
X grent.gr_gid = atoi (grpfields[2]);
X grent.gr_mem = list (grpfields[3]);
X
X return (&grent);
X}
X
Xint
Xputgrent (g, f)
Xstruct group *g;
XFILE *f;
X{
X int i;
X char *cp;
X
X if (! g || ! f)
X return -1;
X
X sprintf (grpbuf, "%s:%s:%d:", g->gr_name, g->gr_passwd, g->gr_gid);
X if (g->gr_mem) {
X cp = strchr (grpbuf, '\0');
X for (i = 0;g->gr_mem[i];i++) {
X if (cp - grpbuf + strlen (g->gr_mem[i]) + 2
X >= sizeof grpbuf)
X return -1;
X
X if (i > 0) {
X strcpy (cp, ",");
X cp = strchr (cp, '\0');
X }
X strcpy (cp, g->gr_mem[i]);
X cp = strchr (cp, '\0');
X }
X strcat (cp, "\n");
X } else
X strcat (grpbuf, "\n");
X
X if (fputs (grpbuf, f) == EOF || ferror (f))
X return -1;
X
X return 0;
X}
SHAR_EOF
if test 1893 -ne "`wc -c < 'grent.c'`"
then
echo shar: "error transmitting 'grent.c'" '(should have been 1893 characters)'
fi
fi
echo shar: "extracting 'chpasswd.c'" '(4902 characters)'
if test -f 'chpasswd.c'
then
echo shar: "will not over-write existing file 'chpasswd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'chpasswd.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X * chpass - update passwords in batch
X *
X * chpass reads standard input for a list of colon separated
X * user names and new passwords. the appropriate password
X * files are updated to reflect the changes. because the
X * changes are made in a batch fashion, the user must run
X * the mkpasswd command after this command terminates since
X * no password updates occur until the very end.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <fcntl.h>
X#include <string.h>
X#include "config.h"
X#ifdef SHADOWPWD
X#include "shadow.h"
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)chpasswd.c 3.1 01:18:53 11/14/90";
X#endif
X
Xchar *Prog;
X
Xextern char *pw_encrypt();
X
X/*
X * If it weren't for the different structures and differences in how
X * certain fields were manipulated, I could just use macros to replace
X * the function calls for the different file formats. So I make the
X * best of things and just use macros to replace a few of the calls.
X */
X
X#ifdef SHADOWPWD
X#define pw_lock spw_lock
X#define pw_open spw_open
X#define pw_close spw_close
X#define pw_unlock spw_unlock
X#endif
X
X/*
X * usage - display usage message and exit
X */
X
Xusage ()
X{
X fprintf (stderr, "usage: %s\n", Prog);
X exit (1);
X}
X
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X char buf[BUFSIZ];
X char *name;
X char *newpwd;
X char *cp;
X#ifdef SHADOWPWD
X struct spwd *sp;
X struct spwd newsp;
X struct spwd *spw_locate();
X#else
X struct passwd *pw;
X struct passwd newpw;
X struct passwd *pw_locate();
X char newage[5];
X#endif
X int errors = 0;
X int line = 0;
X long now = time ((long *) 0) / (24L*3600L);
X
X if (Prog = strrchr (argv[0], '/'))
X Prog++;
X else
X Prog = argv[0];
X
X if (argc != 1)
X usage ();
X
X /*
X * Lock the password file and open it for reading. This will
X * bring all of the entries into memory where they may be
X * updated.
X */
X
X if (! pw_lock ()) {
X fprintf (stderr, "%s: can't lock password file\n", Prog);
X exit (1);
X }
X if (! pw_open (O_RDWR)) {
X fprintf (stderr, "%s: can't open password file\n", Prog);
X exit (1);
X }
X
X /*
X * Read each line, separating the user name from the password.
X * The password entry for each user will be looked up in the
X * appropriate file (shadow or passwd) and the password changed.
X * For shadow files the last change date is set directly, for
X * passwd files the last change date is set in the age only if
X * aging information is present.
X */
X
X while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
X line++;
X if (cp = strrchr (buf, '\n')) {
X *cp = '\0';
X } else {
X fprintf (stderr, "%s: line %d: line too long\n",
X Prog, line);
X errors++;
X continue;
X }
X
X /*
X * The username is the first field. It is separated
X * from the password with a ":" character which is
X * replaced with a NUL to give the new password. The
X * new password will then be encrypted in the normal
X * fashion with a new salt generated.
X */
X
X name = buf;
X if (cp = strchr (name, ':')) {
X *cp++ = '\0';
X } else {
X fprintf (stderr, "%s: line %d: missing new password\n",
X Prog, line);
X errors++;
X continue;
X }
X newpwd = cp;
X cp = pw_encrypt (newpwd, (char *) 0);
X
X /*
X * Get the password file entry for this user. The user
X * must already exist.
X */
X
X#ifdef SHADOWPWD
X if (! (sp = spw_locate (name)))
X#else
X if (! (pw = pw_locate (name)))
X#endif
X {
X fprintf (stderr, "%s: line %d: unknown user %s\n",
X Prog, line, name);
X errors++;
X continue;
X }
X
X /*
X * The freshly encrypted new password is merged into
X * the user's password file entry and the last password
X * change date is set to the current date.
X */
X
X#ifdef SHADOWPWD
X newsp = *sp;
X newsp.sp_pwdp = cp;
X newsp.sp_lstchg = now;
X#else
X newpw = *pw;
X newpw.pw_passwd = cp;
X if (newpw.pw_age[0]) {
X strcpy (newage, newpw.pw_age);
X strcpy (newage + 2, l64a (now / 7));
X newpw.pw_age = newage;
X }
X#endif
X
X /*
X * The updated password file entry is then put back
X * and will be written to the password file later, after
X * all the other entries have been updated as well.
X */
X
X#ifdef SHADOWPWD
X if (! spw_update (&newsp))
X#else
X if (! pw_update (&newpw))
X#endif
X {
X fprintf (stderr, "%s: line %d: cannot update password entry\n",
X Prog, line);
X errors++;
X continue;
X }
X }
X
X /*
X * Any detected errors will cause the entire set of changes
X * to be aborted. Unlocking the password file will cause
X * all of the changes to be ignored. Otherwise the file is
X * closed, causing the changes to be written out all at
X * once, and then unlocked afterwards.
X */
X
X if (errors) {
X fprintf ("%s: error detected, changes ignored\n", Prog);
X pw_unlock ();
X exit (1);
X }
X if (! pw_close ()) {
X fprintf ("%s: error updating password file\n", Prog);
X exit (1);
X }
X (void) pw_unlock ();
X}
SHAR_EOF
if test 4902 -ne "`wc -c < 'chpasswd.c'`"
then
echo shar: "error transmitting 'chpasswd.c'" '(should have been 4902 characters)'
fi
fi
echo shar: "extracting 'shadowio.c'" '(9862 characters)'
if test -f 'shadowio.c'
then
echo shar: "will not over-write existing file 'shadowio.c'"
else
sed 's/^X//' << \SHAR_EOF > 'shadowio.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X * This file implements a transaction oriented password database
X * library. The password file is updated one entry at a time.
X * After each transaction the file must be logically closed and
X * transferred to the existing password file. The sequence of
X * events is
X *
X * spw_lock -- lock shadow file
X * spw_open -- logically open shadow file
X * while transaction to process
X * spw_(locate,update,remove) -- perform transaction
X * done
X * spw_close -- commit transactions
X * spw_unlock -- remove shadow lock
X */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)shadowio.c 3.1 00:51:22 11/14/90";
X#endif
X
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <stdio.h>
X#include "shadow.h"
X
Xstatic int islocked;
Xstatic int isopen;
Xstatic int open_modes;
Xstatic FILE *spwfp;
X
Xstruct spw_file_entry {
X char *spwf_line;
X int spwf_changed;
X struct spwd *spwf_entry;
X struct spw_file_entry *spwf_next;
X};
X
Xstatic struct spw_file_entry *spwf_head;
Xstatic struct spw_file_entry *spwf_tail;
Xstatic struct spw_file_entry *spwf_cursor;
Xstatic int sp_changed;
X
X#define SPW_LOCK "/etc/shadow.lock"
X#define SPW_TEMP "/etc/spwd.%d"
X#define SHADOW "/etc/shadow"
X#define OSHADOW "/etc/shadow-"
X
Xextern char *strdup();
Xextern struct spwd *sgetspent();
X
X/*
X * spw_dup - duplicate a shadow file entry
X *
X * spw_dup() accepts a pointer to a shadow file entry and
X * returns a pointer to a shadow file entry in allocated
X * memory.
X */
X
Xstatic struct spwd *
Xspw_dup (spwd)
Xstruct spwd *spwd;
X{
X struct spwd *spw;
X
X if (! (spw = (struct spwd *) malloc (sizeof *spw)))
X return 0;
X
X *spw = *spwd;
X if ((spw->sp_namp = strdup (spwd->sp_namp)) == 0 ||
X (spw->sp_pwdp = strdup (spwd->sp_pwdp)) == 0)
X return 0;
X
X return spw;
X}
X
X/*
X * spw_free - free a dynamically allocated shadow file entry
X *
X * spw_free() frees up the memory which was allocated for the
X * pointed to entry.
X */
X
Xstatic void
Xspw_free (spwd)
Xstruct spwd *spwd;
X{
X free (spwd->sp_namp);
X free (spwd->sp_pwdp);
X}
X
X/*
X * spw_lock - lock a password file
X *
X * spw_lock() encapsulates the lock operation. it returns
X * TRUE or FALSE depending on the password file being
X * properly locked. the lock is set by creating a semaphore
X * file, SPW_LOCK.
X */
X
Xint
Xspw_lock ()
X{
X int fd;
X int pid;
X int len;
X char file[BUFSIZ];
X char buf[32];
X struct stat sb;
X
X if (islocked)
X return 1;
X
X /*
X * Create a lock file which can be switched into place
X */
X
X sprintf (file, SPW_TEMP, getpid ());
X if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X return 0;
X
X sprintf (buf, "%d", getpid ());
X if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
X (void) close (fd);
X (void) unlink (file);
X return 0;
X }
X close (fd);
X
X /*
X * Simple case first -
X * Link fails (in a sane environment ...) if the target
X * exists already. So we try to switch in a new lock
X * file. If that succeeds, we assume we have the only
X * valid lock. Needs work for NFS where this assumption
X * may not hold. The simple hack is to check the link
X * count on the source file, which should be 2 iff the
X * link =really= worked.
X */
X
X if (link (file, SPW_LOCK) == 0) {
X if (stat (file, &sb) != 0)
X return 0;
X
X if (sb.st_nlink != 2)
X return 0;
X
X (void) unlink (file);
X islocked = 1;
X return 1;
X }
X
X /*
X * Invalid lock test -
X * Open the lock file and see if the lock is valid.
X * The PID of the lock file is checked, and if the PID
X * is not valid, the lock file is removed. If the unlink
X * of the lock file fails, it should mean that someone
X * else is executing this code. They will get success,
X * and we will fail.
X */
X
X if ((fd = open (SPW_LOCK, O_RDWR)) == -1 ||
X (len = read (fd, buf, BUFSIZ)) <= 0) {
X errno = EINVAL;
X return 0;
X }
X buf[len] = '\0';
X if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
X errno = EINVAL;
X return 0;
X }
X if (kill (pid, 0) == 0) {
X errno = EEXIST;
X return 0;
X }
X if (unlink (SPW_LOCK)) {
X (void) close (fd);
X (void) unlink (file);
X
X return 0;
X }
X
X /*
X * Re-try lock -
X * The invalid lock has now been removed and I should
X * be able to acquire a lock for myself just fine. If
X * this fails there will be no retry. The link count
X * test here makes certain someone executing the previous
X * block of code didn't just remove the lock we just
X * linked to.
X */
X
X if (link (file, SPW_LOCK) == 0) {
X if (stat (file, &sb) != 0)
X return 0;
X
X if (sb.st_nlink != 2)
X return 0;
X
X (void) unlink (file);
X islocked = 1;
X return 1;
X }
X (void) unlink (file);
X return 0;
X}
X
X/*
X * spw_unlock - logically unlock a shadow file
X *
X * spw_unlock() removes the lock which was set by an earlier
X * invocation of spw_lock().
X */
X
Xint
Xspw_unlock ()
X{
X if (islocked) {
X if (isopen) {
X open_modes = O_RDONLY;
X (void) spw_close ();
X }
X unlink (SPW_LOCK);
X islocked = 0;
X return 1;
X } else
X return 0;
X}
X
X/*
X * spw_open - open a password file
X *
X * spw_open() encapsulates the open operation. it returns
X * TRUE or FALSE depending on the shadow file being
X * properly opened.
X */
X
Xint
Xspw_open (mode)
Xint mode;
X{
X char buf[8192];
X struct spw_file_entry *spwf;
X struct spwd *spwd;
X
X if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X return 0;
X
X if (mode != O_RDONLY && ! islocked)
X return 0;
X
X if ((spwfp = fopen (SHADOW, mode == O_RDONLY ? "r":"r+")) == 0)
X return 0;
X
X spwf_head = spwf_tail = spwf_cursor = 0;
X sp_changed = 0;
X
X while (fgets (buf, sizeof buf, spwfp) != (char *) 0) {
X if (! (spwf = (struct spw_file_entry *) malloc (sizeof *spwf)))
X return 0;
X
X spwf->spwf_changed = 0;
X spwf->spwf_line = strdup (buf);
X if ((spwd = sgetspent (buf)) && ! (spwd = spw_dup (spwd)))
X return 0;
X
X spwf->spwf_entry = spwd;
X
X if (spwf_head == 0) {
X spwf_head = spwf_tail = spwf;
X spwf->spwf_next = 0;
X } else {
X spwf_tail->spwf_next = spwf;
X spwf->spwf_next = 0;
X spwf_tail = spwf;
X }
X }
X isopen++;
X open_modes = mode;
X
X return 1;
X}
X
X/*
X * spw_close - close the password file
X *
X * spw_close() outputs any modified password file entries and
X * frees any allocated memory.
X */
X
Xint
Xspw_close ()
X{
X int fd;
X int mask;
X int c;
X int i;
X int errors = 0;
X FILE *bkfp;
X struct spw_file_entry *spwf;
X struct spw_file_entry *ospwf;
X
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X if (open_modes == O_RDWR && sp_changed) {
X mask = umask (077);
X if ((bkfp = fopen (OSHADOW, "w")) == 0) {
X umask (mask);
X return 0;
X }
X umask (mask);
X
X rewind (spwfp);
X while ((c = getc (spwfp)) != EOF) {
X if (putc (c, bkfp) == EOF) {
X fclose (bkfp);
X return 0;
X }
X }
X if (fclose (bkfp))
X return 0;
X
X isopen = 0;
X (void) fclose (spwfp);
X
X mask = umask (077);
X if (! (spwfp = fopen (SHADOW, "w"))) {
X umask (mask);
X return 0;
X }
X umask (mask);
X
X for (spwf = spwf_head;errors == 0 && spwf;
X spwf = spwf->spwf_next) {
X if (spwf->spwf_changed) {
X if (putspent (spwf->spwf_entry, spwfp))
X errors++;
X } else {
X if (fputs (spwf->spwf_line, spwfp) == EOF)
X errors++;
X }
X }
X if (fflush (spwfp))
X errors++;
X
X if (errors) {
X unlink (SHADOW);
X link (OSHADOW, SHADOW);
X unlink (OSHADOW);
X return 0;
X }
X }
X if (fclose (spwfp))
X return 0;
X
X spwfp = 0;
X
X while (spwf_head != 0) {
X spwf = spwf_head;
X spwf_head = spwf->spwf_next;
X
X if (spwf->spwf_entry) {
X spw_free (spwf->spwf_entry);
X free (spwf->spwf_entry);
X }
X if (spwf->spwf_line)
X free (spwf->spwf_line);
X
X free (spwf);
X }
X spwf_tail = 0;
X return 1;
X}
X
Xint
Xspw_update (spwd)
Xstruct spwd *spwd;
X{
X struct spw_file_entry *spwf;
X struct spwd *nspwd;
X
X if (! isopen || open_modes == O_RDONLY) {
X errno = EINVAL;
X return 0;
X }
X for (spwf = spwf_head;spwf != 0;spwf = spwf->spwf_next) {
X if (spwf->spwf_entry == 0)
X continue;
X
X if (strcmp (spwd->sp_namp, spwf->spwf_entry->sp_namp) != 0)
X continue;
X
X if (! (nspwd = spw_dup (spwd)))
X return 0;
X else {
X spw_free (spwf->spwf_entry);
X *(spwf->spwf_entry) = *nspwd;
X }
X spwf->spwf_changed = 1;
X spwf_cursor = spwf;
X return sp_changed = 1;
X }
X spwf = (struct spw_file_entry *) malloc (sizeof *spwf);
X if (! (spwf->spwf_entry = spw_dup (spwd)))
X return 0;
X
X spwf->spwf_changed = 1;
X spwf->spwf_next = 0;
X spwf->spwf_line = 0;
X
X if (spwf_tail)
X spwf_tail->spwf_next = spwf;
X
X if (! spwf_head)
X spwf_head = spwf;
X
X spwf_tail = spwf;
X
X return sp_changed = 1;
X}
X
Xint
Xspw_remove (name)
Xchar *name;
X{
X struct spw_file_entry *spwf;
X struct spw_file_entry *ospwf;
X
X if (! isopen || open_modes == O_RDONLY) {
X errno = EINVAL;
X return 0;
X }
X for (ospwf = 0, spwf = spwf_head;spwf != 0;
X ospwf = spwf, spwf = spwf->spwf_next) {
X if (! spwf->spwf_entry)
X continue;
X
X if (strcmp (name, spwf->spwf_entry->sp_namp) != 0)
X continue;
X
X if (spwf == spwf_cursor)
X spwf_cursor = ospwf;
X
X if (ospwf != 0)
X ospwf->spwf_next = spwf->spwf_next;
X else
X spwf_head = spwf->spwf_next;
X
X if (spwf == spwf_tail)
X spwf_tail = ospwf;
X
X return sp_changed = 1;
X }
X errno = ENOENT;
X return 0;
X}
X
Xstruct spwd *
Xspw_locate (name)
Xchar *name;
X{
X struct spw_file_entry *spwf;
X
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X for (spwf = spwf_head;spwf != 0;spwf = spwf->spwf_next) {
X if (spwf->spwf_entry == 0)
X continue;
X
X if (strcmp (name, spwf->spwf_entry->sp_namp) == 0) {
X spwf_cursor = spwf;
X return spwf->spwf_entry;
X }
X }
X errno = ENOENT;
X return 0;
X}
X
Xint
Xspw_rewind ()
X{
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X spwf_cursor = 0;
X return 1;
X}
X
Xstruct spwd *
Xspw_next ()
X{
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X if (spwf_cursor == 0)
X spwf_cursor = spwf_head;
X else
X spwf_cursor = spwf_cursor->spwf_next;
X
X while (spwf_cursor) {
X if (spwf_cursor->spwf_entry)
X return spwf_cursor->spwf_entry;
X
X spwf_cursor = spwf_cursor->spwf_next;
X }
X return 0;
X}
SHAR_EOF
if test 9862 -ne "`wc -c < 'shadowio.c'`"
then
echo shar: "error transmitting 'shadowio.c'" '(should have been 9862 characters)'
fi
fi
echo shar: "extracting 'pwio.c'" '(9982 characters)'
if test -f 'pwio.c'
then
echo shar: "will not over-write existing file 'pwio.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwio.c'
X/*
X * Copyright 1990, John F. Haugh II
X * All rights reserved.
X *
X * Use, duplication, and disclosure prohibited without
X * the express written permission of the author.
X *
X * This file implements a transaction oriented password database
X * library. The password file is updated one entry at a time.
X * After each transaction the file must be logically closed and
X * transferred to the existing password file. The sequence of
X * events is
X *
X * pw_lock -- lock password file
X * pw_open -- logically open password file
X * while transaction to process
X * pw_(locate,update,remove) -- perform transaction
X * done
X * pw_close -- commit transactions
X * pw_unlock -- remove password lock
X */
X
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <pwd.h>
X#include <stdio.h>
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)pwio.c 3.2 01:57:59 11/14/90";
X#endif
X
Xstatic int islocked;
Xstatic int isopen;
Xstatic int open_modes;
Xstatic FILE *pwfp;
X
Xstruct pw_file_entry {
X char *pwf_line;
X int pwf_changed;
X struct passwd *pwf_entry;
X struct pw_file_entry *pwf_next;
X};
X
Xstatic struct pw_file_entry *pwf_head;
Xstatic struct pw_file_entry *pwf_tail;
Xstatic struct pw_file_entry *pwf_cursor;
Xstatic int pw_changed;
X
X#define PW_LOCK "/etc/passwd.lock"
X#define PW_TEMP "/etc/pwd.%d"
X#define PASSWD "/etc/passwd"
X#define OPASSWD "/etc/passwd-"
X
Xextern char *strdup();
Xextern struct passwd *sgetpwent();
X
X/*
X * pw_dup - duplicate a password file entry
X *
X * pw_dup() accepts a pointer to a password file entry and
X * returns a pointer to a password file entry in allocated
X * memory.
X */
X
Xstatic struct passwd *
Xpw_dup (pwent)
Xstruct passwd *pwent;
X{
X struct passwd *pw;
X
X if (! (pw = (struct passwd *) malloc (sizeof *pw)))
X return 0;
X
X if ((pw->pw_name = strdup (pwent->pw_name)) == 0 ||
X (pw->pw_passwd = strdup (pwent->pw_passwd)) == 0 ||
X (pw->pw_age = strdup (pwent->pw_age)) == 0 ||
X (pw->pw_gecos = strdup (pwent->pw_gecos)) == 0 ||
X (pw->pw_dir = strdup (pwent->pw_dir)) == 0 ||
X (pw->pw_shell = strdup (pwent->pw_shell)) == 0)
X return 0;
X
X pw->pw_uid = pwent->pw_uid;
X pw->pw_gid = pwent->pw_gid;
X
X return pw;
X}
X
X/*
X * pw_free - free a dynamically allocated password file entry
X *
X * pw_free() frees up the memory which was allocated for the
X * pointed to entry.
X */
X
Xstatic void
Xpw_free (pwent)
Xstruct passwd *pwent;
X{
X free (pwent->pw_name);
X free (pwent->pw_passwd);
X free (pwent->pw_gecos);
X free (pwent->pw_dir);
X free (pwent->pw_shell);
X}
X
X/*
X * pw_lock - lock a password file
X *
X * pw_lock() encapsulates the lock operation. it returns
X * TRUE or FALSE depending on the password file being
X * properly locked. the lock is set by creating a semaphore
X * file, PW_LOCK.
X */
X
Xint
Xpw_lock ()
X{
X int fd;
X int pid;
X int len;
X char file[BUFSIZ];
X char buf[32];
X struct stat sb;
X
X if (islocked)
X return 1;
X
X /*
X * Create a lock file which can be switched into place
X */
X
X sprintf (file, PW_TEMP, getpid ());
X if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X return 0;
X
X sprintf (buf, "%d", getpid ());
X if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
X (void) close (fd);
X (void) unlink (file);
X return 0;
X }
X close (fd);
X
X /*
X * Simple case first -
X * Link fails (in a sane environment ...) if the target
X * exists already. So we try to switch in a new lock
X * file. If that succeeds, we assume we have the only
X * valid lock. Needs work for NFS where this assumption
X * may not hold. The simple hack is to check the link
X * count on the source file, which should be 2 iff the
X * link =really= worked.
X */
X
X if (link (file, PW_LOCK) == 0) {
X if (stat (file, &sb) != 0)
X return 0;
X
X if (sb.st_nlink != 2)
X return 0;
X
X (void) unlink (file);
X islocked = 1;
X return 1;
X }
X
X /*
X * Invalid lock test -
X * Open the lock file and see if the lock is valid.
X * The PID of the lock file is checked, and if the PID
X * is not valid, the lock file is removed. If the unlink
X * of the lock file fails, it should mean that someone
X * else is executing this code. They will get success,
X * and we will fail.
X */
X
X if ((fd = open (PW_LOCK, O_RDWR)) == -1 ||
X (len = read (fd, buf, BUFSIZ)) <= 0) {
X errno = EINVAL;
X return 0;
X }
X buf[len] = '\0';
X if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
X errno = EINVAL;
X return 0;
X }
X if (kill (pid, 0) == 0) {
X errno = EEXIST;
X return 0;
X }
X if (unlink (PW_LOCK)) {
X (void) close (fd);
X (void) unlink (file);
X
X return 0;
X }
X
X /*
X * Re-try lock -
X * The invalid lock has now been removed and I should
X * be able to acquire a lock for myself just fine. If
X * this fails there will be no retry. The link count
X * test here makes certain someone executing the previous
X * block of code didn't just remove the lock we just
X * linked to.
X */
X
X if (link (file, PW_LOCK) == 0) {
X if (stat (file, &sb) != 0)
X return 0;
X
X if (sb.st_nlink != 2)
X return 0;
X
X (void) unlink (file);
X islocked = 1;
X return 1;
X }
X (void) unlink (file);
X return 0;
X}
X
X/*
X * pw_unlock - logically unlock a password file
X *
X * pw_unlock() removes the lock which was set by an earlier
X * invocation of pw_lock().
X */
X
Xint
Xpw_unlock ()
X{
X if (islocked) {
X if (isopen) {
X open_modes = O_RDONLY;
X pw_close ();
X }
X unlink (PW_LOCK);
X islocked = 0;
X return 1;
X } else
X return 0;
X}
X
X/*
X * pw_open - open a password file
X *
X * pw_open() encapsulates the open operation. it returns
X * TRUE or FALSE depending on the password file being
X * properly opened.
X */
X
Xint
Xpw_open (mode)
Xint mode;
X{
X char buf[8192];
X struct pw_file_entry *pwf;
X struct passwd *pwent;
X
X if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X return 0;
X
X if (mode != O_RDONLY && ! islocked)
X return 0;
X
X if ((pwfp = fopen (PASSWD, mode == O_RDONLY ? "r":"r+")) == 0)
X return 0;
X
X pwf_head = pwf_tail = pwf_cursor = 0;
X pw_changed = 0;
X
X while (fgets (buf, sizeof buf, pwfp) != (char *) 0) {
X if (! (pwf = (struct pw_file_entry *) malloc (sizeof *pwf)))
X return 0;
X
X pwf->pwf_changed = 0;
X pwf->pwf_line = strdup (buf);
X if ((pwent = sgetpwent (buf)) && ! (pwent = pw_dup (pwent)))
X return 0;
X
X pwf->pwf_entry = pwent;
X
X if (pwf_head == 0) {
X pwf_head = pwf_tail = pwf;
X pwf->pwf_next = 0;
X } else {
X pwf_tail->pwf_next = pwf;
X pwf->pwf_next = 0;
X pwf_tail = pwf;
X }
X }
X isopen++;
X open_modes = mode;
X
X return 1;
X}
X
X/*
X * pw_close - close the password file
X *
X * pw_close() outputs any modified password file entries and
X * frees any allocated memory.
X */
X
Xint
Xpw_close ()
X{
X int fd;
X int mask;
X int c;
X int i;
X int errors = 0;
X FILE *bkfp;
X struct pw_file_entry *pwf;
X struct pw_file_entry *opwf;
X
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X if (open_modes == O_RDWR && pw_changed) {
X mask = umask (022);
X if ((bkfp = fopen (OPASSWD, "w")) == 0) {
X umask (mask);
X return 0;
X }
X umask (mask);
X
X rewind (pwfp);
X while ((c = getc (pwfp)) != EOF) {
X if (putc (c, bkfp) == EOF) {
X fclose (bkfp);
X return 0;
X }
X }
X if (fclose (bkfp))
X return 0;
X
X isopen = 0;
X (void) fclose (pwfp);
X
X mask = umask (022);
X if (! (pwfp = fopen (PASSWD, "w"))) {
X umask (mask);
X return 0;
X }
X umask (mask);
X
X for (pwf = pwf_head;errors == 0 && pwf;pwf = pwf->pwf_next) {
X if (pwf->pwf_changed) {
X if (putpwent (pwf->pwf_entry, pwfp))
X errors++;
X } else {
X if (fputs (pwf->pwf_line, pwfp) == EOF)
X errors++;
X }
X }
X if (fflush (pwfp))
X errors++;
X
X if (errors) {
X unlink (PASSWD);
X link (OPASSWD, PASSWD);
X unlink (OPASSWD);
X return 0;
X }
X }
X if (fclose (pwfp))
X return 0;
X
X pwfp = 0;
X
X while (pwf_head != 0) {
X pwf = pwf_head;
X pwf_head = pwf->pwf_next;
X
X if (pwf->pwf_entry) {
X pw_free (pwf->pwf_entry);
X free (pwf->pwf_entry);
X }
X if (pwf->pwf_line)
X free (pwf->pwf_line);
X
X free (pwf);
X }
X pwf_tail = 0;
X return 1;
X}
X
Xint
Xpw_update (pwent)
Xstruct passwd *pwent;
X{
X struct pw_file_entry *pwf;
X struct passwd *npw;
X
X if (! isopen || open_modes == O_RDONLY) {
X errno = EINVAL;
X return 0;
X }
X for (pwf = pwf_head;pwf != 0;pwf = pwf->pwf_next) {
X if (pwf->pwf_entry == 0)
X continue;
X
X if (strcmp (pwent->pw_name, pwf->pwf_entry->pw_name) != 0)
X continue;
X
X if (! (npw = pw_dup (pwent)))
X return 0;
X else {
X pw_free (pwf->pwf_entry);
X *(pwf->pwf_entry) = *npw;
X }
X pwf->pwf_changed = 1;
X pwf_cursor = pwf;
X return pw_changed = 1;
X }
X pwf = (struct pw_file_entry *) malloc (sizeof *pwf);
X if (! (pwf->pwf_entry = pw_dup (pwent)))
X return 0;
X
X pwf->pwf_changed = 1;
X pwf->pwf_next = 0;
X pwf->pwf_line = 0;
X
X if (pwf_tail)
X pwf_tail->pwf_next = pwf;
X
X if (! pwf_head)
X pwf_head = pwf;
X
X pwf_tail = pwf;
X
X return pw_changed = 1;
X}
X
Xint
Xpw_remove (name)
Xchar *name;
X{
X struct pw_file_entry *pwf;
X struct pw_file_entry *opwf;
X
X if (! isopen || open_modes == O_RDONLY) {
X errno = EINVAL;
X return 0;
X }
X for (opwf = 0, pwf = pwf_head;pwf != 0;
X opwf = pwf, pwf = pwf->pwf_next) {
X if (! pwf->pwf_entry)
X continue;
X
X if (strcmp (name, pwf->pwf_entry->pw_name) != 0)
X continue;
X
X if (pwf == pwf_cursor)
X pwf_cursor = opwf;
X
X if (opwf != 0)
X opwf->pwf_next = pwf->pwf_next;
X else
X pwf_head = pwf->pwf_next;
X
X if (pwf == pwf_tail)
X pwf_tail = opwf;
X
X return pw_changed = 1;
X }
X errno = ENOENT;
X return 0;
X}
X
Xstruct passwd *
Xpw_locate (name)
Xchar *name;
X{
X struct pw_file_entry *pwf;
X
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X for (pwf = pwf_head;pwf != 0;pwf = pwf->pwf_next) {
X if (pwf->pwf_entry == 0)
X continue;
X
X if (strcmp (name, pwf->pwf_entry->pw_name) == 0) {
X pwf_cursor = pwf;
X return pwf->pwf_entry;
X }
X }
X errno = ENOENT;
X return 0;
X}
X
Xint
Xpw_rewind ()
X{
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X pwf_cursor = 0;
X return 1;
X}
X
Xstruct passwd *
Xpw_next ()
X{
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X if (pwf_cursor == 0)
X pwf_cursor = pwf_head;
X else
X pwf_cursor = pwf_cursor->pwf_next;
X
X while (pwf_cursor) {
X if (pwf_cursor->pwf_entry)
X return pwf_cursor->pwf_entry;
X
X pwf_cursor = pwf_cursor->pwf_next;
X }
X return 0;
X}
SHAR_EOF
if test 9982 -ne "`wc -c < 'pwio.c'`"
then
echo shar: "error transmitting 'pwio.c'" '(should have been 9982 characters)'
fi
fi
exit 0
# End of shell archive
--
John F. Haugh II UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 832-8832 Domain: jfh at rpp386.cactus.org
"SCCS, the source motel! Programs check in and never check out!"
-- Ken Thompson
More information about the Alt.sources
mailing list