home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume26
/
shadow
/
part05
< prev
next >
Wrap
Text File
|
1991-11-24
|
55KB
|
2,601 lines
Newsgroups: comp.sources.misc
From: jfh@rpp386.Cactus.ORG (John F Haugh II)
Subject: v26i058: shadow - Shadow Password Suite, Part05/11
Message-ID: <1991Nov24.185026.20213@sparky.imd.sterling.com>
X-Md4-Signature: 1e593b97d7d71d67542aae12605dbe91
Date: Sun, 24 Nov 1991 18:50:26 GMT
Approved: kent@sparky.imd.sterling.com
Submitted-by: jfh@rpp386.Cactus.ORG (John F Haugh II)
Posting-number: Volume 26, Issue 58
Archive-name: shadow/part05
Environment: UNIX
Supersedes: shadow-2: Volume 06, Issue 22-24
#! /bin/sh
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents: age.c groupio.c newgrp.c pwio.c shadowio.c
# Wrapped by kent@sparky on Sun Nov 24 11:03:42 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 5 (of 11)."'
if test -f 'age.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'age.c'\"
else
echo shar: Extracting \"'age.c'\" \(6178 characters\)
sed "s/^X//" >'age.c' <<'END_OF_FILE'
X/*
X * Copyright 1989, 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <errno.h>
X#include "config.h"
X#include "pwd.h"
X#include "shadow.h"
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)age.c 3.5 07:43:04 9/17/91";
X#endif
X
X#define DAY (24L*3600L)
X#ifdef ITI_AGING
X#define SCALE (DAY)
X#else
X#define SCALE (1)
X#endif
X
Xextern time_t time ();
Xextern char *strdup();
X
X/*
X * pwd_to_spwd - create entries for new spwd structure
X *
X * pwd_to_spwd() creates a new (struct spwd) containing the
X * information in the pointed-to (struct passwd).
X */
X
Xstatic struct spwd *
Xpwd_to_spwd (pw)
Xstruct passwd *pw;
X{
X static struct spwd tspwd;
X struct spwd *sp = &tspwd;
X time_t t;
X
X /*
X * Nice, easy parts first. The name and passwd map directly
X * from the old password structure to the new one.
X */
X
X sp->sp_namp = strdup (pw->pw_name);
X sp->sp_pwdp = strdup (pw->pw_passwd);
X#ifdef ATT_AGE
X
X /*
X * AT&T-style password aging maps the sp_min, sp_max, and
X * sp_lstchg information from the pw_age field, which appears
X * after the encrypted password.
X */
X
X if (pw->pw_age[0]) {
X t = (c64i (pw->pw_age[0]) * 7) * SCALE;
X sp->sp_max = t;
X
X if (pw->pw_age[1]) {
X t = (c64i (pw->pw_age[1]) * 7) * SCALE;
X sp->sp_min = t;
X } else
X sp->sp_min = (10000L) * SCALE;
X
X if (pw->pw_age[1] && pw->pw_age[2]) {
X t = (a64l (pw->pw_age + 2) * 7) * SCALE;
X sp->sp_lstchg = t;
X } else
X sp->sp_lstchg = time ((time_t *) 0) / (DAY/SCALE);
X } else {
X sp->sp_min = 0;
X sp->sp_max = (10000L * SCALE);
X sp->sp_lstchg = time ((time_t *) 0) / (DAY/SCALE);
X }
X#else /* !ATT_AGE */
X /*
X * BSD does not use the pw_age field and has no aging information
X * anywheres. The default values are used to initialize the
X * fields which are in the missing pw_age field;
X */
X
X sp->sp_min = 0;
X sp->sp_max = (10000L * SCALE);
X sp->sp_lstchg = time ((time_t *) 0) / (DAY/SCALE);
X#endif /* ATT_AGE */
X
X /*
X * These fields have no corresponding information in the password
X * file. They are set to uninitialized values.
X */
X
X sp->sp_warn = -1;
X sp->sp_inact = -1;
X sp->sp_expire = -1;
X sp->sp_flag = -1;
X
X return sp;
X}
X
X/*
X * isexpired - determine if account is expired yet
X *
X * isexpired calculates the expiration date based on the
X * password expiration criteria.
X */
X
X/*ARGSUSED*/
Xint
Xisexpired (pw, sp)
Xstruct passwd *pw;
Xstruct spwd *sp;
X{
X long clock;
X
X clock = time ((time_t *) 0) / (DAY/SCALE);
X
X /*
X * Quick and easy - there is an expired account field
X * along with an inactive account field. Do the expired
X * one first since it is worse.
X */
X
X if (sp->sp_expire > 0 && sp->sp_expire < clock)
X return 3;
X
X if (sp->sp_inact > 0 && sp->sp_lstchg > 0 && sp->sp_max > 0 &&
X sp->sp_inact + sp->sp_lstchg + sp->sp_max < clock)
X return 2;
X
X /*
X * The last and max fields must be present for an account
X * to have an expired password. A maximum of >10000 days
X * is considered to be infinite.
X */
X
X if (sp->sp_lstchg == -1 ||
X sp->sp_max == -1 || sp->sp_max >= (10000L*SCALE))
X return 0;
X
X /*
X * Calculate today's day and the day on which the password
X * is going to expire. If that date has already passed,
X * the password has expired.
X */
X
X if (sp->sp_lstchg + sp->sp_max < clock)
X return 1;
X
X return 0;
X}
X
X/*
X * expire - force password change if password expired
X *
X * expire() calls /bin/passwd to change the user's password
X * if it has expired.
X */
X
Xint
Xexpire (pw, sp)
Xstruct passwd *pw;
Xstruct spwd *sp;
X{
X int status;
X int child;
X int pid;
X
X if (! sp)
X sp = pwd_to_spwd (pw);
X
X /*
X * See if the user's password has expired, and if so
X * force them to change their password.
X */
X
X switch (status = isexpired (pw, sp)) {
X case 0:
X return 0;
X case 1:
X printf ("Your password has expired.");
X break;
X case 2:
X printf ("Your password is inactive.");
X break;
X case 3:
X printf ("Your login has expired.");
X break;
X }
X
X /*
X * Setting the maximum valid period to less than the minimum
X * valid period means that the minimum period will never
X * occur while the password is valid, so the user can never
X * change that password.
X */
X
X if (status > 1 || sp->sp_max < sp->sp_min) {
X puts (" Contact the system administrator.\n");
X exit (1);
X }
X puts (" Choose a new one.\n");
X fflush (stdout);
X
X /*
X * Close all the files so that unauthorized access won't
X * occur. This needs to be done anyway because those files
X * might become stale after "passwd" is executed.
X */
X
X endspent ();
X endpwent ();
X endsgent ();
X endgrent ();
X
X /*
X * Execute the /bin/passwd command. The exit status will be
X * examined to see what the result is. If there are any
X * errors the routine will exit. This forces the user to
X * change their password before being able to use the account.
X */
X
X if ((pid = fork ()) == 0) {
X execl ("/bin/passwd", "passwd", pw->pw_name, (char *) 0);
X puts ("Can't execute /bin/passwd");
X exit (errno);
X } else if (pid == -1) {
X perror ("passwd");
X exit (errno);
X }
X while ((child = wait (&status)) != pid && child != -1)
X ;
X
X if (child == pid && status == 0)
X return 1;
X
X exit (1);
X /*NOTREACHED*/
X}
X
X/*
X * agecheck - see if warning is needed for password expiration
X *
X * agecheck sees how many days until the user's password is going
X * to expire and warns the user of the pending password expiration.
X */
X
Xvoid
Xagecheck (pw, sp)
Xstruct passwd *pw;
Xstruct spwd *sp;
X{
X long clock = time ((long *) 0) / (DAY/SCALE);
X long remain;
X
X if (! sp)
X sp = pwd_to_spwd (pw);
X
X /*
X * The last, max, and warn fields must be supported or the
X * warning period cannot be calculated.
X */
X
X if (sp->sp_lstchg == -1 || sp->sp_max == -1 || sp->sp_warn == -1)
X return;
X
X if ((remain = (sp->sp_lstchg + sp->sp_max) - clock) <= sp->sp_warn) {
X remain /= SCALE;
X if (remain >= 0)
X printf ("Your password will expire in %d %s.\n",
X remain, remain == 1 ? "day":"days");
X }
X}
END_OF_FILE
if test 6178 -ne `wc -c <'age.c'`; then
echo shar: \"'age.c'\" unpacked with wrong size!
fi
# end of 'age.c'
fi
if test -f 'groupio.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'groupio.c'\"
else
echo shar: Extracting \"'groupio.c'\" \(11029 characters\)
sed "s/^X//" >'groupio.c' <<'END_OF_FILE'
X/*
X * Copyright 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X *
X * This file implements a transaction oriented group database
X * library. The group file is updated one entry at a time.
X * After each transaction the file must be logically closed and
X * transferred to the existing group file. The sequence of
X * events is
X *
X * gr_lock -- lock group file
X * gr_open -- logically open group file
X * while transaction to process
X * gr_(locate,update,remove) -- perform transaction
X * done
X * gr_close -- commit transactions
X * gr_unlock -- remove group lock
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <grp.h>
X#include <stdio.h>
X#ifdef BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)groupio.c 3.9 08:45:35 9/12/91";
X#endif
X
Xstatic int islocked;
Xstatic int isopen;
Xstatic int open_modes;
Xstatic FILE *grfp;
X
Xstruct gr_file_entry {
X char *grf_line;
X int grf_changed;
X struct group *grf_entry;
X struct gr_file_entry *grf_next;
X};
X
Xstatic struct gr_file_entry *grf_head;
Xstatic struct gr_file_entry *grf_tail;
Xstatic struct gr_file_entry *grf_cursor;
Xstatic int gr_changed;
Xstatic int lock_pid;
X
X#define GR_LOCK "/etc/group.lock"
X#define GR_TEMP "/etc/grp.%d"
X#define GROUP "/etc/group"
X
Xstatic char gr_filename[BUFSIZ] = GROUP;
X
Xextern char *strdup();
Xextern struct group *sgetgrent();
Xextern char *malloc();
Xextern char *fgetsx();
X
X/*
X * gr_dup - duplicate a group file entry
X *
X * gr_dup() accepts a pointer to a group file entry and
X * returns a pointer to a group file entry in allocated
X * memory.
X */
X
Xstatic struct group *
Xgr_dup (grent)
Xstruct group *grent;
X{
X struct group *gr;
X int i;
X
X if (! (gr = (struct group *) malloc (sizeof *gr)))
X return 0;
X
X if ((gr->gr_name = strdup (grent->gr_name)) == 0 ||
X (gr->gr_passwd = strdup (grent->gr_passwd)) == 0)
X return 0;
X
X for (i = 0;grent->gr_mem[i];i++)
X ;
X
X gr->gr_mem = (char **) malloc (sizeof (char *) * (i + 1));
X for (i = 0;grent->gr_mem[i];i++)
X if (! (gr->gr_mem[i] = strdup (grent->gr_mem[i])))
X return 0;
X
X gr->gr_mem[i] = 0;
X gr->gr_gid = grent->gr_gid;
X
X return gr;
X}
X
X/*
X * gr_free - free a dynamically allocated group file entry
X *
X * gr_free() frees up the memory which was allocated for the
X * pointed to entry.
X */
X
Xstatic void
Xgr_free (grent)
Xstruct group *grent;
X{
X int i;
X
X free (grent->gr_name);
X free (grent->gr_passwd);
X
X for (i = 0;grent->gr_mem[i];i++)
X free (grent->gr_mem[i]);
X
X free ((char *) grent->gr_mem);
X}
X
X/*
X * gr_name - change the name of the group file
X */
X
Xint
Xgr_name (name)
Xchar *name;
X{
X if (isopen || strlen (name) > (BUFSIZ-10))
X return -1;
X
X strcpy (gr_filename, name);
X return 0;
X}
X
X/*
X * gr_lock - lock a group file
X *
X * gr_lock() encapsulates the lock operation. it returns
X * TRUE or FALSE depending on the group file being
X * properly locked. the lock is set by creating a semaphore
X * file, GR_LOCK.
X */
X
Xint
Xgr_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 if (strcmp (gr_filename, GROUP) != 0)
X return 0;
X
X /*
X * Create a lock file which can be switched into place
X */
X
X sprintf (file, GR_TEMP, lock_pid = getpid ());
X if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X return 0;
X
X sprintf (buf, "%d", lock_pid);
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, GR_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 (GR_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 (GR_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, GR_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 * gr_unlock - logically unlock a group file
X *
X * gr_unlock() removes the lock which was set by an earlier
X * invocation of gr_lock().
X */
X
Xint
Xgr_unlock ()
X{
X if (isopen) {
X open_modes = O_RDONLY;
X if (! gr_close ())
X return 0;
X }
X if (islocked) {
X islocked = 0;
X if (lock_pid != getpid ())
X return 0;
X
X (void) unlink (GR_LOCK);
X return 1;
X }
X return 0;
X}
X
X/*
X * gr_open - open a group file
X *
X * gr_open() encapsulates the open operation. it returns
X * TRUE or FALSE depending on the group file being
X * properly opened.
X */
X
Xint
Xgr_open (mode)
Xint mode;
X{
X char buf[8192];
X char *cp;
X struct gr_file_entry *grf;
X struct group *grent;
X
X if (isopen || (mode != O_RDONLY && mode != O_RDWR))
X return 0;
X
X if (mode != O_RDONLY && ! islocked &&
X strcmp (gr_filename, GROUP) == 0)
X return 0;
X
X if ((grfp = fopen (gr_filename, mode == O_RDONLY ? "r":"r+")) == 0)
X return 0;
X
X grf_head = grf_tail = grf_cursor = 0;
X gr_changed = 0;
X
X while (fgetsx (buf, sizeof buf, grfp) != (char *) 0) {
X if (cp = strrchr (buf, '\n'))
X *cp = '\0';
X
X if (! (grf = (struct gr_file_entry *) malloc (sizeof *grf)))
X return 0;
X
X grf->grf_changed = 0;
X grf->grf_line = strdup (buf);
X if ((grent = sgetgrent (buf)) && ! (grent = gr_dup (grent)))
X return 0;
X
X grf->grf_entry = grent;
X
X if (grf_head == 0) {
X grf_head = grf_tail = grf;
X grf->grf_next = 0;
X } else {
X grf_tail->grf_next = grf;
X grf->grf_next = 0;
X grf_tail = grf;
X }
X }
X isopen++;
X open_modes = mode;
X
X return 1;
X}
X
X/*
X * gr_close - close the group file
X *
X * gr_close() outputs any modified group file entries and
X * frees any allocated memory.
X */
X
Xint
Xgr_close ()
X{
X char backup[BUFSIZ];
X int mask;
X int c;
X int errors = 0;
X FILE *bkfp;
X struct gr_file_entry *grf;
X struct stat sb;
X
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X if (islocked && lock_pid != getpid ()) {
X isopen = 0;
X islocked = 0;
X errno = EACCES;
X return 0;
X }
X strcpy (backup, gr_filename);
X strcat (backup, "-");
X
X if (open_modes == O_RDWR && gr_changed) {
X mask = umask (0222);
X if ((bkfp = fopen (backup, "w")) == 0) {
X umask (mask);
X return 0;
X }
X umask (mask);
X fstat (fileno (grfp), &sb);
X chown (backup, sb.st_uid, sb.st_gid);
X
X rewind (grfp);
X while ((c = getc (grfp)) != 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 (grfp);
X
X mask = umask (0222);
X if (! (grfp = fopen (gr_filename, "w"))) {
X umask (mask);
X return 0;
X }
X umask (mask);
X
X for (grf = grf_head;! errors && grf;grf = grf->grf_next) {
X if (grf->grf_changed) {
X if (putgrent (grf->grf_entry, grfp))
X errors++;
X } else {
X if (fputsx (grf->grf_line, grfp))
X errors++;
X
X if (putc ('\n', grfp) == EOF)
X errors++;
X }
X }
X if (fflush (grfp))
X errors++;
X
X if (errors) {
X unlink (gr_filename);
X link (backup, gr_filename);
X unlink (backup);
X return 0;
X }
X }
X if (fclose (grfp))
X return 0;
X
X grfp = 0;
X
X while (grf_head != 0) {
X grf = grf_head;
X grf_head = grf->grf_next;
X
X if (grf->grf_entry) {
X gr_free (grf->grf_entry);
X free ((char *) grf->grf_entry);
X }
X if (grf->grf_line)
X free (grf->grf_line);
X
X free ((char *) grf);
X }
X grf_tail = 0;
X isopen = 0;
X return 1;
X}
X
Xint
Xgr_update (grent)
Xstruct group *grent;
X{
X struct gr_file_entry *grf;
X struct group *ngr;
X
X if (! isopen || open_modes == O_RDONLY) {
X errno = EINVAL;
X return 0;
X }
X for (grf = grf_head;grf != 0;grf = grf->grf_next) {
X if (grf->grf_entry == 0)
X continue;
X
X if (strcmp (grent->gr_name, grf->grf_entry->gr_name) != 0)
X continue;
X
X if (! (ngr = gr_dup (grent)))
X return 0;
X else {
X gr_free (grf->grf_entry);
X *(grf->grf_entry) = *ngr;
X }
X grf->grf_changed = 1;
X grf_cursor = grf;
X return gr_changed = 1;
X }
X grf = (struct gr_file_entry *) malloc (sizeof *grf);
X if (! (grf->grf_entry = gr_dup (grent)))
X return 0;
X
X grf->grf_changed = 1;
X grf->grf_next = 0;
X grf->grf_line = 0;
X
X if (grf_tail)
X grf_tail->grf_next = grf;
X
X if (! grf_head)
X grf_head = grf;
X
X grf_tail = grf;
X
X return gr_changed = 1;
X}
X
Xint
Xgr_remove (name)
Xchar *name;
X{
X struct gr_file_entry *grf;
X struct gr_file_entry *ogrf;
X
X if (! isopen || open_modes == O_RDONLY) {
X errno = EINVAL;
X return 0;
X }
X for (ogrf = 0, grf = grf_head;grf != 0;
X ogrf = grf, grf = grf->grf_next) {
X if (! grf->grf_entry)
X continue;
X
X if (strcmp (name, grf->grf_entry->gr_name) != 0)
X continue;
X
X if (grf == grf_cursor)
X grf_cursor = ogrf;
X
X if (ogrf != 0)
X ogrf->grf_next = grf->grf_next;
X else
X grf_head = grf->grf_next;
X
X if (grf == grf_tail)
X grf_tail = ogrf;
X
X return gr_changed = 1;
X }
X errno = ENOENT;
X return 0;
X}
X
Xstruct group *
Xgr_locate (name)
Xchar *name;
X{
X struct gr_file_entry *grf;
X
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X for (grf = grf_head;grf != 0;grf = grf->grf_next) {
X if (grf->grf_entry == 0)
X continue;
X
X if (strcmp (name, grf->grf_entry->gr_name) == 0) {
X grf_cursor = grf;
X return grf->grf_entry;
X }
X }
X errno = ENOENT;
X return 0;
X}
X
Xint
Xgr_rewind ()
X{
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X grf_cursor = 0;
X return 1;
X}
X
Xstruct group *
Xgr_next ()
X{
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X if (grf_cursor == 0)
X grf_cursor = grf_head;
X else
X grf_cursor = grf_cursor->grf_next;
X
X while (grf_cursor) {
X if (grf_cursor->grf_entry)
X return grf_cursor->grf_entry;
X
X grf_cursor = grf_cursor->grf_next;
X }
X return 0;
X}
END_OF_FILE
if test 11029 -ne `wc -c <'groupio.c'`; then
echo shar: \"'groupio.c'\" unpacked with wrong size!
fi
# end of 'groupio.c'
fi
if test -f 'newgrp.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'newgrp.c'\"
else
echo shar: Extracting \"'newgrp.c'\" \(10165 characters\)
sed "s/^X//" >'newgrp.c' <<'END_OF_FILE'
X/*
X * Copyright 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
X */
X
X#include <sys/types.h>
X#ifndef BSD
X#include <string.h>
X#else
X#include <strings.h>
X#endif
X#include <stdio.h>
X#include <grp.h>
X#include "pwd.h"
X#include <termio.h>
X#ifdef SYS3
X#include <sys/ioctl.h>
X#endif
X#include "config.h"
X
X#if !defined(BSD) && !defined(SUN)
X#define bzero(p,n) memset(p, 0, n)
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)newgrp.c 3.7 08:43:39 9/12/91";
X#endif
X
X#ifdef NGROUPS
Xint ngroups;
Xgid_t groups[NGROUPS];
X#endif
X
Xchar *getpass();
Xchar *getenv();
Xchar *pw_encrypt();
Xstruct passwd *pwd;
Xstruct passwd *getpwuid();
Xstruct passwd *getpwnam();
X
X#ifdef SHADOWPWD
X#include "shadow.h"
Xstruct spwd *spwd;
Xstruct spwd *getspnam();
X#endif
X#ifdef SHADOWGRP
Xstruct sgrp *sgrp;
Xstruct sgrp *getsgnam();
X#endif
Xstruct group *grp;
Xstruct group *getgrgid();
Xstruct group *getgrnam();
X
Xchar *getlogin();
Xchar *crypt();
Xchar *getpass();
Xchar *getenv();
Xchar *pw_encrypt();
Xvoid shell();
X
Xchar *name;
Xchar *group;
Xint gid;
X
Xchar *Prog;
Xchar prog[BUFSIZ];
Xchar base[BUFSIZ];
Xchar passwd[BUFSIZ];
Xchar *cpasswd;
Xchar *salt;
X
X#ifndef MAXENV
X#define MAXENV 64
X#endif
X
Xchar *newenvp[MAXENV];
Xint newenvc = 0;
Xint maxenv = MAXENV;
X
X/*
X * usage - print command usage message
X */
X
Xusage ()
X{
X fprintf (stderr, "usage: newgrp [ - ] [ group ]\n");
X}
X
X/*
X * newgrp - change the invokers current real and effective group id
X */
X
Xmain (argc, argv, envp)
Xint argc;
Xchar **argv;
Xchar **envp;
X{
X int initflag = 0;
X int needspasswd = 0;
X int i;
X char *cp;
X
X /*
X * save my name for error messages and save my real gid incase
X * of errors. if there is an error i have to exec a new login
X * shell for the user since her old shell won't have fork'd to
X * create the process. skip over the program name to the next
X * command line argument.
X */
X
X Prog = argv[0];
X gid = getgid ();
X argc--; argv++;
X
X /*
X * let me parse the command line first. the only recognized
X * option is a "-", which indicates that the shell is to perform
X * the same initialization it does at login time. the next
X * argument, if present, must be the new group name. any
X * remaining arguments will be used to execute a command for
X * the user as the named group. if the group name isn't present
X * i just use the login group id of this user.
X */
X
X if (argc > 0 && argv[0][0] == '-') {
X if (strcmp (argv[0], "-") == 0) {
X initflag = 1;
X argc--; argv++;
X } else {
X usage ();
X goto failure;
X }
X }
X#ifdef NGROUPS
X
X /*
X * get the current users groupset. the new group will be
X * added to the concurrent groupset if there is room, otherwise
X * you get a nasty message but at least your real and effective
X * group id's are set.
X */
X
X ngroups = getgroups (groups);
X#endif
X
X /*
X * now i get to determine my current name. i do this to validate
X * my access to the requested group. the validation works like
X * this -
X * 1) get the name associated with my current user id
X * 2) get my login name, as told by getlogin().
X * 3) if they match, my name is the login name
X * 4) if they don't match, my name is the name in the
X * password file.
X *
X * this isn't perfect, but it works more often then not.
X */
X
X pwd = getpwuid (getuid ());
X
X if (! (name = getlogin ()) || strcmp (name, pwd->pw_name) != 0)
X name = pwd->pw_name;
X
X if (! (pwd = getpwnam (name))) {
X fprintf (stderr, "unknown user: %s\n", name);
X exit (1);
X }
X
X /*
X * now we determine the name of the new group which she wishes
X * to become a member of. the password file entry for her
X * current user id has been gotten. if there is no optional
X * group argument she will have her real and effective group id
X * set to the value from her password file entry. otherwise
X * we validate her access to the specified group.
X */
X
X if (argv[0] != (char *) 0) {
X
X /*
X * start by getting the entry for the requested group.
X */
X
X if (! (grp = getgrnam (group = argv[0]))) {
X fprintf (stderr, "unknown group: %s\n", group);
X goto failure;
X }
X#ifdef SHADOWGRP
X sgrp = getsgnam (group);
X#endif
X
X /*
X * see if she is a member of this group.
X */
X
X for (i = 0;grp->gr_mem[i];i++)
X if (strcmp (name, grp->gr_mem[i]) == 0)
X break;
X
X /*
X * if she isn't a member, she needs to provide the
X * group password. if there is no group password, she
X * will be denied access anyway.
X */
X
X if (grp->gr_mem[i] == (char *) 0)
X needspasswd = 1;
X
X#ifdef SHADOWGRP
X if (sgrp) {
X
X /*
X * Do the tests again with the shadow group entry.
X */
X
X for (i = 0;sgrp->sg_mem[i];i++)
X if (strcmp (name, sgrp->sg_mem[i]) == 0)
X break;
X
X needspasswd = sgrp->sg_mem[i] == 0;
X }
X#endif
X#ifdef SHADOWPWD
X
X /*
X * if she does not have either a shadowed password,
X * or a regular password, and the group has a password,
X * she needs to give the group password.
X */
X
X if (spwd = getspnam (name)) {
X if (spwd->sp_pwdp[0] == '\0' && grp->gr_passwd[0])
X needspasswd = 1;
X#ifdef SHADOWGRP
X if (spwd->sp_pwdp[0] == '\0' && sgrp != 0)
X needspasswd = sgrp->sg_passwd[0] != '\0';
X#endif
X } else {
X if (pwd->pw_passwd[0] == '\0' && grp->gr_passwd[0])
X needspasswd = 1;
X#ifdef SHADOWGRP
X if (pwd->pw_passwd[0] == '\0' && sgrp != 0)
X needspasswd = sgrp->sg_passwd[0] != '\0';
X#endif
X }
X#else
X
X /*
X * if she does not have a regular password she will have
X * to give the group password, if one exists.
X */
X
X if (pwd->pw_passwd[0] == '\0' && grp->gr_passwd[0])
X needspasswd = 1;
X#ifdef SHADOWGRP
X if (pwd->pw_passwd[0] == '\0' && sgrp != 0)
X needspasswd = sgrp->sg_passwd[0] != '\0';
X#endif
X#endif
X
X /*
X * Skip over the user name
X */
X
X argc--; argv++;
X
X } else {
X
X /*
X * get the group file entry for her login group id.
X * the entry must exist, simply to be annoying.
X */
X
X if (! (grp = getgrgid (pwd->pw_gid))) {
X fprintf (stderr, "unknown gid: %d\n", pwd->pw_gid);
X goto failure;
X }
X }
X
X /*
X * now i see about letting her into the group she requested.
X * if she is the root user, i'll let her in without having to
X * prompt for the password. otherwise i ask for a password
X * if she flunked one of the tests above. note that she
X * won't have to provide the password to her login group even
X * if she isn't listed as a member.
X */
X
X if (getuid () != 0 && needspasswd) {
X char *encrypted;
X
X encrypted = grp->gr_passwd;
X#ifdef SHADOWGRP
X if (sgrp)
X encrypted = sgrp->sg_passwd;
X#endif
X passwd[0] = '\0';
X
X if (encrypted[0]) {
X
X /*
X * get the password from her, and set the salt for
X * the decryption from the group file.
X */
X
X if (! (cp = getpass ("Password:")))
X goto failure;
X
X strcpy (passwd, cp);
X bzero (cp, strlen (cp));
X salt = encrypted;
X } else {
X
X /*
X * there is no password, print out "Sorry" and give up
X */
X
X fputs ("Sorry\n", stderr);
X goto failure;
X }
X
X /*
X * encrypt the key she gave us using the salt from
X * the password in the group file. the result of
X * this encryption must match the previously
X * encrypted value in the file.
X */
X
X cpasswd = pw_encrypt (passwd, salt);
X bzero (passwd, sizeof passwd);
X
X if (strcmp (cpasswd, encrypted) != 0) {
X fputs ("Sorry\n", stderr);
X goto failure;
X }
X }
X
X /*
X * all successful validations pass through this point. the
X * group id will be set, and the group added to the concurrent
X * groupset.
X */
X
X gid = grp->gr_gid;
X#ifdef NGROUPS
X
X /*
X * i am going to try to add her new group id to her concurrent
X * group set. if the group id is already present i'll just
X * skip this part. if the group doesn't fit, i'll complain
X * loudly and skip this part ...
X */
X
X for (i = 0;i < ngroups;i++) {
X if (gid == groups[i])
X break;
X }
X if (i == ngroups) {
X if (ngroups == NGROUPS) {
X fprintf (stderr, "too many groups\n");
X } else {
X groups[ngroups++] = gid;
X if (setgroups (ngroups, groups)) {
X fprintf (stderr, "%s: ", Prog);
X perror ("unable to set groups");
X }
X }
X }
X#endif
X
X /*
X * this is where all failures land. the group id will not
X * have been set, so the setgid() below will set me to the
X * original group id i had when i was invoked.
X */
X
Xfailure:
X
X /*
X * i set her group id either to the value she requested, or
X * to the original value. i have to go back to the original
X * because she no longer has a shell running.
X */
X
X if (setgid (gid))
X perror ("setgid");
X
X if (setuid (getuid ()))
X perror ("setuid");
X
X /*
X * i have to get the pathname of her login shell. as a favor
X * i'll try her environment for a $SHELL value first, and
X * then try the password file entry.
X */
X
X if (! initflag && (cp = getenv ("SHELL")))
X strncpy (prog, cp, sizeof prog);
X else if (pwd->pw_shell && pwd->pw_shell[0])
X strncpy (prog, pwd->pw_shell, sizeof prog);
X else
X strcpy (prog, "/bin/sh");
X
X /*
X * now i try to find the basename of the login shell. this
X * will become argv[0] of the spawned command.
X */
X
X if (cp = strrchr (prog, '/'))
X cp++;
X else
X cp = prog;
X
X /*
X * to have the shell perform login processing i will set the
X * first character in the first argument to a "-".
X */
X
X if (initflag)
X strcat (strcpy (base, "-"), cp);
X else
X strcpy (base, cp);
X
X#ifdef SHADOWPWD
X endspent ();
X#endif
X#ifdef SHADOWGRP
X endsgent ();
X#endif
X endpwent ();
X endgrent ();
X
X /*
X * switch back to her home directory if i am doing login
X * initialization.
X */
X
X if (initflag) {
X chdir (pwd->pw_dir);
X while (*envp) {
X if (strncmp (*envp, "PATH=", 5) == 0 ||
X strncmp (*envp, "HOME=", 5) == 0 ||
X strncmp (*envp, "SHELL=", 6) == 0)
X addenv (*envp);
X
X envp++;
X }
X } else {
X while (*envp)
X addenv (*envp++);
X }
X
X /*
X * exec the login shell and go away. if there were additional
X * arguments, use those instead.
X */
X
X if (argc > 0) {
X argv--;
X argv[0] = prog;
X execve (argv[0], argv, newenvp);
X perror (argv[0]);
X _exit (127);
X }
X shell (prog, base);
X /*NOTREACHED*/
X}
END_OF_FILE
if test 10165 -ne `wc -c <'newgrp.c'`; then
echo shar: \"'newgrp.c'\" unpacked with wrong size!
fi
# end of 'newgrp.c'
fi
if test -f 'pwio.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'pwio.c'\"
else
echo shar: Extracting \"'pwio.c'\" \(11226 characters\)
sed "s/^X//" >'pwio.c' <<'END_OF_FILE'
X/*
X * Copyright 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
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/types.h>
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include "pwd.h"
X#include <stdio.h>
X
X#ifdef BSD
X# include <strings.h>
X#else
X# include <string.h>
X#endif
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)pwio.c 3.9 08:46:13 9/12/91";
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;
Xstatic int lock_pid;
X
X#define PW_LOCK "/etc/passwd.lock"
X#define PW_TEMP "/etc/pwd.%d"
X#define PASSWD "/etc/passwd"
X
Xstatic char pw_filename[BUFSIZ] = PASSWD;
X
Xextern int fputs();
Xextern char *fgets();
Xextern char *strdup();
Xextern char *malloc();
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#ifdef ATT_AGE
X (pw->pw_age = strdup (pwent->pw_age)) == 0 ||
X#endif /* ATT_AGE */
X#ifdef ATT_COMMENT
X (pw->pw_comment = strdup (pwent->pw_comment)) == 0 ||
X#endif /* ATT_COMMENT */
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_name - change the name of the password file
X */
X
Xint
Xpw_name (name)
Xchar *name;
X{
X if (isopen || strlen (name) > (BUFSIZ-10))
X return -1;
X
X strcpy (pw_filename, name);
X return 0;
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 if (strcmp (pw_filename, PASSWD) != 0)
X return 0;
X
X /*
X * Create a lock file which can be switched into place
X */
X
X sprintf (file, PW_TEMP, lock_pid = getpid ());
X if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X return 0;
X
X sprintf (buf, "%d", lock_pid);
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 (isopen) {
X open_modes = O_RDONLY;
X if (! pw_close ())
X return 0;
X }
X if (islocked) {
X islocked = 0;
X if (lock_pid != getpid ())
X return 0;
X
X (void) unlink (PW_LOCK);
X return 1;
X }
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 char *cp;
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 strcmp (pw_filename, PASSWD) == 0)
X return 0;
X
X if ((pwfp = fopen (pw_filename, 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 (cp = strrchr (buf, '\n'))
X *cp = '\0';
X
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 char backup[BUFSIZ];
X int mask;
X int c;
X int errors = 0;
X FILE *bkfp;
X struct pw_file_entry *pwf;
X struct stat sb;
X
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X if (islocked && lock_pid != getpid ()) {
X isopen = 0;
X islocked = 0;
X errno = EACCES;
X return 0;
X }
X strcpy (backup, pw_filename);
X strcat (backup, "-");
X
X if (open_modes == O_RDWR && pw_changed) {
X mask = umask (0222);
X if ((bkfp = fopen (backup, "w")) == 0) {
X umask (mask);
X return 0;
X }
X umask (mask);
X fstat (fileno (pwfp), &sb);
X chown (backup, sb.st_uid, sb.st_gid);
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 (0222);
X if (! (pwfp = fopen (pw_filename, "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 if (putc ('\n', pwfp) == EOF)
X errors++;
X }
X }
X if (fflush (pwfp))
X errors++;
X
X if (errors) {
X unlink (pw_filename);
X link (backup, pw_filename);
X unlink (backup);
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 isopen = 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}
END_OF_FILE
if test 11226 -ne `wc -c <'pwio.c'`; then
echo shar: \"'pwio.c'\" unpacked with wrong size!
fi
# end of 'pwio.c'
fi
if test -f 'shadowio.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'shadowio.c'\"
else
echo shar: Extracting \"'shadowio.c'\" \(10934 characters\)
sed "s/^X//" >'shadowio.c' <<'END_OF_FILE'
X/*
X * Copyright 1990, 1991, John F. Haugh II
X * All rights reserved.
X *
X * Permission is granted to copy and create derivative works for any
X * non-commercial purpose, provided this copyright notice is preserved
X * in all copies of source code, or included in human readable form
X * and conspicuously displayed on all copies of object code or
X * distribution media.
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.6 09:17:38 6/26/91";
X#endif
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <stdio.h>
X#ifdef BSD
X#include <strings.h>
X#else
X#include <string.h>
X#endif
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;
Xstatic int lock_pid;
X
X#define SPW_LOCK "/etc/shadow.lock"
X#define SPW_TEMP "/etc/spwd.%d"
X#define SHADOW "/etc/shadow"
X
Xstatic char spw_filename[BUFSIZ] = SHADOW;
X
Xextern char *strdup();
Xextern char *malloc();
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_name - change the name of the shadow password file
X */
X
Xint
Xspw_name (name)
Xchar *name;
X{
X if (isopen || strlen (name) > (BUFSIZ-10))
X return -1;
X
X strcpy (spw_filename, name);
X return 0;
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 if (strcmp (spw_filename, SHADOW) != 0)
X return 0;
X
X /*
X * Create a lock file which can be switched into place
X */
X
X sprintf (file, SPW_TEMP, lock_pid = getpid ());
X if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
X return 0;
X
X sprintf (buf, "%d", lock_pid);
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 (isopen) {
X open_modes = O_RDONLY;
X if (! spw_close ())
X return 0;
X }
X if (islocked) {
X islocked = 0;
X if (lock_pid != getpid ())
X return 0;
X
X (void) unlink (SPW_LOCK);
X return 1;
X }
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[BUFSIZ];
X char *cp;
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 strcmp (spw_filename, SHADOW) == 0)
X return 0;
X
X if ((spwfp = fopen (spw_filename, 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 (cp = strrchr (buf, '\n'))
X *cp = '\0';
X
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 char backup[BUFSIZ];
X int mask;
X int c;
X int errors = 0;
X FILE *bkfp;
X struct spw_file_entry *spwf;
X struct stat sb;
X
X if (! isopen) {
X errno = EINVAL;
X return 0;
X }
X if (islocked && lock_pid != getpid ()) {
X isopen = 0;
X islocked = 0;
X errno = EACCES;
X return 0;
X }
X strcpy (backup, spw_filename);
X strcat (backup, "-");
X
X if (open_modes == O_RDWR && sp_changed) {
X mask = umask (0377);
X if ((bkfp = fopen (backup, "w")) == 0) {
X umask (mask);
X return 0;
X }
X umask (mask);
X fstat (fileno (spwfp), &sb);
X chown (backup, sb.st_uid, sb.st_gid);
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 (0377);
X if (! (spwfp = fopen (spw_filename, "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 if (putc ('\n', spwfp) == EOF)
X errors++;
X }
X }
X if (fflush (spwfp))
X errors++;
X
X if (errors) {
X unlink (spw_filename);
X link (backup, spw_filename);
X unlink (backup);
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 isopen = 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}
END_OF_FILE
if test 10934 -ne `wc -c <'shadowio.c'`; then
echo shar: \"'shadowio.c'\" unpacked with wrong size!
fi
# end of 'shadowio.c'
fi
echo shar: End of archive 5 \(of 11\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 11 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...