Clone of chfn(1)
John F. Haugh II
jfh at rpp386.cactus.org
Fri Aug 3 15:56:25 AEST 1990
The next program in a long series ...
[ This command is derived from the shadow password commands I've been
posting for the last few years. It is the latest addition to a
collection of commands which deal with /etc/passwd and friends.
This particular command is a straight ripoff of the passwd(1)
command I posted last year and uses a few of the files from that
program suite. ]
The chfn(1) command is used to modify the GCOS field of a user's
password file entry. Typically this field contains the user's
full name, office number, and various telephone extensions. There
is an additional feature in that fields containing "="'s are
collected together and shoved off to the end of the GCOS field.
This is for support of the ulimit=, umask=, and pri= features of
the shadow login command.
There is a manpage - I've been a good boy lately ;-). You will
want to begin by editing the config.h file - you won't need most
of the #defines that are in there. The Makefile is for the entire
package, so just type "make chfn" - ignore the rest of it. You
will then have to install the software in /bin as uid=root,
mode=4111, or something like that.
The really tricky #define is DBM. The DBM routines are very rude
and print there error messages on stderr, rather than just passing
back error codes. I plan on doing something to get around the
problem (like improve my error checking ...). Unless you really
want the DBM stuff, undefine DBM.
And without further nonsense, I give you chfn!
Unshar and enjoy.
----- cut here -----
#! /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:
# Makefile
# config.h
# chfn.c
# chfn.1
# pwent.c
# pwpack.c
# This archive created: Fri Aug 3 00:43:49 1990
# By: John F. Haugh II (River Parishes Programming, Austin TX)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'Makefile'" '(7265 characters)'
if test -f 'Makefile'
then
echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X#
X# Copyright 1988,1989,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# @(#)Makefile 2.11 00:43:36 - Shadow password system
X#
X# @(#)Makefile 2.11 00:43:36 8/3/90
X#
XSHELL = /bin/sh
X
X# Define the directory login is copied to. BE VERY CAREFUL!!!
X# LOGINDIR = /bin
XLOGINDIR = /etc
X
X# Pick your favorite C compiler and tags command
XCC = cc
XTAGS = ctags
X
X# OS. Currently only BSD and USG are defined. If you don't use BSD,
X# USG (System V) is assumed.
X# OS = -DBSD
X
X# Do you have to do ranlib? Sorry to hear that ...
XRANLIB = ranlib
X# RANLIB = echo
X
X# Flags for SCO Xenix/386
XCFLAGS = -O -M3 -g $(PWDEF) $(AL64DEF) $(OS)
XLIBS = -lcrypt -ldbm
XLDFLAGS = -M3 -g
XLTFLAGS =
X# This should be Slibsec.a for small model, or Llibsec.a for
X# large model or whatever. MUST AGREE WITH CFLAGS!!!
XLIBSEC = Slibsec.a
X
X# Flags for normal machines
X# CFLAGS = -O -g $(PWDEF) $(AL64DEF) $(OS)
X# LIBS =
X# LDFLAGS = -g
X# LIBSEC = libsec.a
X
XLOBJS = lmain.o login.o env.o password.o entry.o valid.o setup.o shell.o age.o \
X pwent.o utmp.o sub.o mail.o motd.o log.o shadow.o dialup.o dialchk.o \
X ttytype.o failure.o port.o pwpack.o
X
XLSRCS = lmain.c login.c env.c password.c entry.c valid.c setup.c shell.c age.c \
X pwent.c utmp.c sub.c mail.c motd.c log.c shadow.c dialup.c dialchk.c \
X ttytype.c failure.c port.c pwpack.c
X
XSOBJS = smain.o env.o password.o entry.o suvalid.o susetup.o sushell.o \
X pwent.o susub.o mail.o motd.o sulog.o shadow.o age.o pwpack.o
X
XSSRCS = smain.c env.c password.c entry.c valid.c setup.c shell.c \
X pwent.c sub.c mail.c motd.c sulog.c shadow.c age.c pwpack.c
X
XPOBJS = pmain.o password.o entry.o valid.o pwage.o pwent.o obscure.o shadow.o \
X pwpack.o
X
XPSRCS = pmain.c password.c entry.c valid.c age.c pwent.c obscure.c shadow.c \
X pwpack.c
X
XGPSRCS = gpmain.c password.c grent.c
X
XGPOBJS = gpmain.o password.o grent.o
X
XPWOBJS = pwconv.o pwent.o shadow.o pwage.o pwpack.o
X
XPWSRCS = pwconv.c pwent.c shadow.c age.c pwpack.c
X
XPWUNOBJS = pwunconv.o pwent.o shadow.o pwage.o pwpack.o
X
XPWUNSRCS = pwunconv.c pwent.c shadow.c age.c pwpack.c
X
XSULOGOBJS = sulogin.o entry.o env.o password.o pwage.o pwent.o setup.o \
X shadow.o shell.o valid.o pwpack.o
X
XSULOGSRCS = sulogin.c entry.c env.c password.c age.c pwent.c setup.c \
X shadow.c shell.c valid.c pwpack.c
X
XDBOBJS = mkpasswd.o pwent.o pwpack.o
X
XDBSRCS = mkpasswd.c pwent.c pwpack.c
X
XNGSRCS = newgrp.c shadow.c password.c
X
XNGOBJS = newgrp.o shadow.o password.o
X
XCHFNSRCS = chfn.c pwent.c pwpack.c
X
XCHFNOBJS = chfn.o pwent.o pwpack.o
X
XALLSRCS = age.c dialchk.c dialup.c entry.c env.c lmain.c log.c login.c mail.c \
X motd.c obscure.c password.c pmain.c pwconv.c pwent.c pwunconv.c \
X setup.c shadow.c shell.c smain.c sub.c sulog.c sulogin.c ttytype.c \
X utmp.c valid.c port.c newgrp.c gpmain.c grent.c mkpasswd.c pwpack.c \
X chfn.c
X
XFILES1 = README log.c mail.c shadow.h sulog.c Makefile entry.c obscure.c \
X setup.c sub.c config.h pmain.c sulogin.c dialup.h ttytype.c \
X port.c newgrp.c
X
XFILES2 = lastlog.h login.c motd.c password.c shell.c utmp.c age.c env.c \
X pwent.c shadow.c valid.c lmain.c smain.c pwconv.c dialup.c dialchk.c \
X pwunconv.c failure.c faillog.h faillog.c port.h gpmain.c grent.c \
X mkpasswd.c pwpack.c chfn.c
X
XMAN_1 = chfn.1 login.1 passwd.1 su.1
XMAN_3 = shadow.3
XMAN_4 = faillog.4 passwd.4 shadow.4
XMAN_8 = faillog.8 pwconv.8 pwunconv.8 sulogin.8
X
XDOCS = $(MAN_1) $(MAN_3) $(MAN_4) $(MAN_8)
X
XBINS = su login pwconv pwunconv passwd sulogin faillog newgrp gpasswd \
X mkpasswd chfn
X
Xall: $(BINS) $(DOCS)
X
Xlibsec: shadow.o
X ar rv $(LIBSEC) shadow.o
X $(RANLIB) $(LIBSEC)
X
Xinstall: all
X strip $(BINS)
X cp login $(LOGINDIR)/login
X cp mkpasswd pwconv pwunconv sulogin /etc
X cp su passwd gpasswd faillog newgrp chfn /bin
X cp shadow.h /usr/include
X chown root $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \
X /bin/su /bin/passwd /bin/gpasswd /bin/newgrp /etc/mkpasswd
X chgrp root $(LOGINDIR)/login /etc/pwconv /etc/pwunconv /etc/sulogin \
X /bin/su /bin/passwd /bin/gpasswd /bin/newgrp /etc/mkpasswd
X chown bin /bin/faillog /usr/include/shadow.h
X chgrp bin /bin/faillog /usr/include/shadow.h
X chmod 700 /etc/pwconv /etc/pwunconv /etc/sulogin /etc/mkpasswd
X chmod 4711 $(LOGINDIR)/login /bin/su /bin/passwd /bin/gpasswd \
X /bin/newgrp /bin/chfn
X chmod 711 /bin/faillog
X chmod 444 /usr/include/shadow.h
X
Xlint: su.L login.L pwconv.L pwunconv.L passwd.L sulogin.L faillog.L \
X newgrp.L gpasswd.L mkpasswd.L chfn.L
X
Xtags: $(ALLSRCS)
X $(TAGS) $(ALLSRCS)
X
XREADME: s.README
X get s.README
X
X$(DOCS):
X get s.$@
X
Xlogin: $(LOBJS)
X $(CC) -o login $(LDFLAGS) $(LOBJS) $(LIBS)
X
Xlogin.L: $(LSRCS)
X lint $(LSRCS) > login.L
X
Xsu: $(SOBJS)
X $(CC) -o su $(LDFLAGS) $(SOBJS) $(LIBS)
X
Xsu.L: $(SSRCS)
X lint -DSU $(SSRCS) > su.L
X
Xpasswd: $(POBJS)
X $(CC) -o passwd $(LDFLAGS) $(POBJS) $(LIBS)
X
Xpasswd.L: $(PSRCS)
X lint -DPASSWD $(PSRCS) > passwd.L
X
Xgpasswd: $(GPOBJS)
X $(CC) -o gpasswd $(LDFLAGS) $(GPOBJS) $(LIBS)
X
Xgpasswd.L: $(GPSRCS)
X lint $(GPSRCS) > gpasswd.L
X
Xpwconv: $(PWOBJS)
X $(CC) -o pwconv $(LDFLAGS) $(PWOBJS) $(LIBS)
X
Xpwconv.L: $(PWSRCS)
X lint -DPASSWD $(PWSRCS) > pwconv.L
X
Xpwunconv: $(PWUNOBJS)
X $(CC) -o pwunconv $(LDFLAGS) $(PWUNOBJS) $(LIBS)
X
Xpwunconv.L: $(PWUNSRCS)
X lint -DPASSWD $(PWUNSRCS) > pwunconv.L
X
Xsulogin: $(SULOGOBJS)
X $(CC) -o sulogin $(LDFLAGS) $(SULOGOBJS) $(LIBS)
X
Xsulogin.L: $(SULOGSRCS)
X lint $(SULOGSRCS) > sulogin.L
X
Xfaillog: faillog.o
X $(CC) -o faillog $(LDFLAGS) faillog.o $(LIBS)
X
Xfaillog.L: faillog.c faillog.h config.h
X lint faillog.c > faillog.L
X
Xmkpasswd: $(DBOBJS)
X $(CC) -o mkpasswd $(LDFLAGS) $(DBOBJS) $(LIBS)
X
Xmkpasswd.L: $(DBSRCS)
X lint $(DBSRCS) > mkpasswd.L
X
Xnewgrp: $(NGOBJS)
X $(CC) -o newgrp $(LDFLAGS) $(NGOBJS) $(LIBS)
X
Xnewgrp.L: $(NGSRCS)
X lint $(NGSRCS) > newgrp.L
X
Xchfn: $(CHFNOBJS)
X $(CC) -o chfn $(LDFLAGS) $(CHFNOBJS) $(LIBS)
X
Xchfn.L: $(CHFNSRCS)
X lint $(CHFNSRCS) > chfn.L
X
Xsushell.c: shell.c
X cp shell.c sushell.c
X
Xsushell.o: config.h sushell.c
X $(CC) -c $(CFLAGS) -DSU sushell.c
X
Xsusub.c: sub.c
X cp sub.c susub.c
X
Xsusub.o: config.h susub.c
X $(CC) -c $(CFLAGS) -DSU susub.c
X
Xsulog.o: config.h
X
Xsusetup.c: setup.c
X cp setup.c susetup.c
X
Xsusetup.o: config.h setup.c
X $(CC) -c $(CFLAGS) -DSU susetup.c
X
Xsuvalid.c: valid.c
X cp valid.c suvalid.c
X
Xsuvalid.o: config.h valid.c
X $(CC) -c $(CFLAGS) -DSU suvalid.c
X
Xpmain.o: config.h lastlog.h shadow.h
X
Xpwage.o: age.c config.h
X cp age.c pwage.c
X $(CC) -c $(CFLAGS) -DPASSWD pwage.c
X rm pwage.c
X
Xlmain.o: config.h lastlog.h faillog.h
X
Xsmain.o: config.h lastlog.h
X
Xsetup.o: config.h
X
Xutmp.o: config.h
X
Xmail.o: config.h
X
Xmotd.o: config.h
X
Xage.o: config.h
X
Xlog.o: config.h lastlog.h
X
Xshell.o: config.h
X
Xentry.o: config.h shadow.h
X
Xshadow.o: shadow.h
X
Xdialup.o: dialup.h
X
Xdialchk.o: dialup.h config.h
X
Xvalid.o: config.h
X
Xfailure.o: faillog.h config.h
X
Xfaillog.o: faillog.h config.h
X
Xpwent.o: config.h
X
Xport.o: port.h
X
Xnewgrp.o: config.h shadow.h
X
Xmkpasswd.o: config.h
X
Xchfn.o: config.h
X
Xclean:
X -rm -f *.o a.out core npasswd nshadow *.pag *.dir
X
Xclobber: clean
X -rm -f $(BINS) *.L
X
Xshar: login.sh.1 login.sh.2 login.sh.3
X
Xlogin.sh.1: $(FILES1)
X shar -a $(FILES1) > login.sh.1
X
Xlogin.sh.2: $(FILES2)
X shar -a $(FILES2) > login.sh.2
X
Xlogin.sh.3: $(DOCS)
X shar -a $(DOCS) > login.sh.3
SHAR_EOF
if test 7265 -ne "`wc -c < 'Makefile'`"
then
echo shar: "error transmitting 'Makefile'" '(should have been 7265 characters)'
fi
fi
echo shar: "extracting 'config.h'" '(5646 characters)'
if test -f 'config.h'
then
echo shar: "will not over-write existing file 'config.h'"
else
sed 's/^X//' << \SHAR_EOF > 'config.h'
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.
X *
X * @(#)config.h 2.4 19:23:34 7/29/90
X */
X
X/*
X * Define DIALUP to use dialup password files
X */
X
X#define DIALUP
X
X/*
X * Define SHADOWPWD to use shadow [ unreadable ] password file
X */
X
X#define SHADOWPWD
X
X/*
X * Define DOUBLESIZE to use 16 character passwords
X */
X
X#define DOUBLESIZE
X
X/*
X * Define OBSCURE to include hard password testing code.
X */
X
X#define OBSCURE
X
X/*
X * Define PASSLENGTH to be shortest legal password
X */
X
X#define PASSLENGTH 5
X
X/*
X * Define NOBLANK if you want all passwords prompted for, including
X * empty ones.
X
X#undef NOBLANK
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 NDEBUG for production versions
X */
X
X#define NDEBUG
X
X/*
X * Define HZ if login must set HZ value
X */
X
X#define HZ "HZ=50"
X
X/*
X * Define TZ if login must set timezone
X *
X * The first example sets the variable directly. The
X * second example names a file which is read to determine
X * the proper value. The file consists of a single line
X * of the form 'TZ=zone-name'
X */
X
X/* #define TZ "TZ=CST6CDT" */
X#define TZ "/etc/tzname"
X
X/*
X * Define the default PATH and SUPATH here. PATH is for non-privileged
X * users, SUPATH is for root. The first pair are for real trusting
X * systems, the second pair are for the paranoid ...
X */
X
X/* #define PATH "PATH=:/bin:/usr/bin" */
X/* #define SUPATH "PATH=:/bin:/usr/bin:/etc" */
X#define PATH "PATH=/bin:/usr/bin"
X#define SUPATH "PATH=/bin:/usr/bin:/etc"
X
X/*
X * Define the mailbox directory
X */
X
X#define MAILDIR "/usr/spool/mail/"
X
X/*
X * Define AGING if you want the password aging checks made.
X */
X
X#define AGING
X
X/*
X * Define MAILCHECK if you want the mailbox checked for new mail
X *
X * One of two messages are printed - `You have new mail.' or
X * `You have mail.'.
X */
X
X#define MAILCHECK
X
X/*
X * Define CONSOLE if you want ROOT restricted to a particular terminal.
X *
X * Use the name of the tty line if you only want a single line, or use
X * the name of the file containing the permissible ports if you wish to
X * allow root logins on more than one port.
X */
X
X/* #define CONSOLE "console" /* root on /dev/console only */
X#define CONSOLE "/etc/consoles" /* check /etc/consoles for a list */
X
X/*
X * Define NOLOGINS if you want to be able to deny non-root users logins.
X * Logins will not be permitted if this file exists.
X */
X
X#define NOLOGINS "/etc/nologin"
X
X/*
X * Define NOUSE if you want to be able to declare accounts which can't
X * be logged into. Define NOLOGIN if you want it to be an su-only account.
X */
X
X#define NOUSE "NOUSE"
X#define NOLOGIN "NOLOGIN"
X
X/*
X * Define MOTD if you want the message of the day (/etc/motd) printed
X * at login time.
X */
X
X#define MOTD
X
X/*
X * Define HUSHLOGIN if you want the code added to avoid printing the
X * motd if a file $HOME/.hushlogin exists. This obviously only matters
X * if any of MOTD, MAILCHECK or LASTLOG are #define'd.
X */
X
X#define HUSHLOGIN
X
X/*
X * Define LASTLOG if you want a record made of logins in /usr/adm/lastlog.
X */
X
X#define LASTLOG
X
X/*
X * Define FAILLOG if you want a record make of failed logins in
X * /usr/adm/faillog. See faillog.h for more details. See fail(1L)
X * for even still more details ... Also, define FTMP to record utmp
X * style records for failed logins. FTMP is the name of a utmp-like
X * file. You can use who(1) instead of faillog(L), which is an
X * advantage.
X */
X
X#define FAILLOG
X#define FTMP "/etc/ftmp"
X
X/*
X * Define TTYPERM to be the initial terminal permissions. Defining
X * as 0600 will not allow messages, 0622 will.
X */
X
X#define TTYPERM 0600
X
X/*
X * Define TTYTYPE to the be name of the port to terminal type
X * mapping file. This is used to set the environmental variable
X * "TERM" to the correct terminal type.
X */
X
X#define TTYTYPE "/etc/ttytype"
X
X/*
X * Define QUOTAS if you want the code added in setup.c to support
X * file ulimit and nice [ and umask as well ] setting from the password
X * file.
X */
X
X#define QUOTAS
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 * Define file name for sulog. If SULOG is not defined, there will be
X * no logging. This is NOT a good idea ... We also define other file
X * names.
X */
X
X#define SULOG "/usr/adm/sulog"
X#define SUCON "/dev/console"
X#define PWDFILE "/etc/passwd"
X#define OPWDFILE "/etc/-passwd"
X#define NPWDFILE "/etc/npasswd"
X#define OSHADOW "/etc/-shadow"
X#define NSHADOW "/etc/nshadow"
X#define GRPFILE "/etc/group"
X#define OGRPFILE "/etc/-group"
X#define NGRPFILE "/etc/ngroup"
X
X/*
X * Define PWDLOCK to be a locking semaphore for updating the password
X * file. GRPLOCK is the same for the group file.
X */
X
X#define PWDLOCK "/etc/.pwdlock"
X#define GRPLOCK "/etc/.grplock"
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 ERASECHAR '\b'
X#define KILLCHAR '\025'
X#define UMASK 022
X
X#define ULIMIT (1L<<20) /* Define if your UNIX supports ulimit() */
X#define FGETPWENT /* Define if library does not include FGETPWENT */
X#define GETPWENT /* Define if you want my GETPWENT(3) routines */
X#define NEED_AL64 /* Define if library does not include a64l() */
SHAR_EOF
if test 5646 -ne "`wc -c < 'config.h'`"
then
echo shar: "error transmitting 'config.h'" '(should have been 5646 characters)'
fi
fi
echo shar: "extracting 'chfn.c'" '(10425 characters)'
if test -f 'chfn.c'
then
echo shar: "will not over-write existing file 'chfn.c'"
else
sed 's/^X//' << \SHAR_EOF > 'chfn.c'
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#include <sys/types.h>
X#include <stdio.h>
X#include <pwd.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <errno.h>
X#include <ctype.h>
X#ifndef BSD
X#include <string.h>
X#include <memory.h>
X#else
X#include <strings.h>
X#define strchr index
X#define strrchr rindex
X#endif
X#include "config.h"
X
X#ifdef DBM
X#include <dbm.h>
X#endif
X
X#ifndef lint
Xstatic char _sccsid[] = "@(#)chfn.c 1.1 08:07:55 8/1/90";
X#endif
X
Xchar *myname;
X
Xchar user[BUFSIZ];
Xchar fullnm[BUFSIZ];
Xchar roomno[BUFSIZ];
Xchar workph[BUFSIZ];
Xchar homeph[BUFSIZ];
Xchar slop[BUFSIZ];
Xint fflg;
Xint rflg;
Xint wflg;
Xint hflg;
Xint oflg;
X
Xstruct passwd pwent;
X
Xextern int errno;
X
Xchar Usage[] =
X"Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ] [ -h home_ph ]\n";
X
Xchar Usage_root[] =
X"Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ]\n\
X [ -h home_ph ] [ -o other ] [ user ]\n";
X
X/*
X * usage - print command line syntax and exit
X */
X
Xvoid
Xusage ()
X{
X fprintf (stderr, getuid () == 0 ? Usage_root:Usage, myname);
X exit (1);
X}
X
X/*
X * valid_field - insure that a field contains all legal characters
X *
X * The supplied field is scanned for non-printing and other illegal
X * characters. If any illegal characters are found, valid_field
X * prints a message and exits.
X */
X
Xvoid
Xvalid_field (field, illegal)
Xchar *field;
Xchar *illegal;
X{
X char *cp;
X
X for (cp = field;*cp && isprint (*cp) && ! strchr (illegal, *cp);cp++)
X ;
X
X if (*cp) {
X fprintf (stderr, "%s: invalid field: %s\n", myname, field);
X exit (1);
X }
X}
X
X/*
X * change_field - change a single field if a new value is given.
X *
X * prompt the user with the name of the field being changed and the
X * current value.
X */
X
Xvoid
Xchange_field (buf, prompt)
Xchar *buf;
Xchar *prompt;
X{
X char new[BUFSIZ];
X char *cp;
X
X printf ("\t%s [%s]: ", prompt, buf);
X fgets (new, BUFSIZ, stdin);
X
X if (cp = strchr (new, '\n'))
X *cp = '\0';
X else
X return;
X
X if (new[0])
X strcpy (buf, new);
X}
X
X/*
X * new_fields - change the user's GECOS information interactively
X *
X * prompt the user for each of the four fields and fill in the fields
X * from the user's response, or leave alone if nothing was entered.
X */
X
Xnew_fields ()
X{
X printf ("Enter the new value, or press return for the default\n\n");
X
X change_field (fullnm, "Full Name");
X change_field (roomno, "Room Number");
X change_field (workph, "Work Phone");
X change_field (homeph, "Home Phone");
X
X if (getuid () == 0)
X change_field (slop, "Other");
X}
X
X/*
X * copy_field - get the next field from the gecos field
X *
X * copy_field copies the next field from the gecos field, returning a
X * pointer to the field which follows, or NULL if there are no more
X * fields.
X */
X
Xchar *
Xcopy_field (in, out, extra)
Xchar *in; /* the current GECOS field */
Xchar *out; /* where to copy the field to */
Xchar *extra; /* fields with '=' get copied here */
X{
X char *cp;
X
X while (in) {
X if (cp = strchr (in, ','))
X *cp++ = '\0';
X
X if (! strchr (in, '='))
X break;
X
X if (extra) {
X if (extra[0])
X strcat (extra, ",");
X
X strcat (extra, in);
X }
X in = cp;
X }
X if (in && out)
X strcpy (out, in);
X
X return cp;
X}
X
X#ifdef DBM
X/*
X * update_dbm
X *
X * Updates the DBM password files, if they exist.
X */
X
Xupdate_dbm (pw)
Xstruct passwd *pw;
X{
X datum key;
X datum content;
X char data[BUFSIZ];
X int len;
X
X len = pw_pack (pw, data);
X content.dsize = len;
X content.dptr = data;
X
X key.dsize = strlen (pw->pw_name);
X key.dptr = pw->pw_name;
X store (key, content);
X
X key.dsize = sizeof pw->pw_uid;
X key.dptr = (char *) &pw->pw_uid;
X store (key, content);
X}
X#endif
X
Xint
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X extern int optind;
X extern char *optarg;
X void die ();
X char *cp;
X char *getlogin ();
X int amroot;
X int lockfd = -1;
X int flag;
X struct passwd *pw;
X struct passwd *getpwuid ();
X struct passwd *getpwnam ();
X struct passwd *sgetpwent ();
X FILE *npwd;
X FILE *pwd;
X char buf[BUFSIZ];
X char tmp[BUFSIZ];
X
X amroot = getuid () == 0;
X if (myname = strchr (argv[0], '/'))
X myname++;
X else
X myname = argv[0];
X
X while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
X switch (flag) {
X case 'f':
X fflg++;
X strcpy (fullnm, optarg);
X break;
X case 'r':
X rflg++;
X strcpy (roomno, optarg);
X break;
X case 'w':
X wflg++;
X strcpy (workph, optarg);
X break;
X case 'h':
X hflg++;
X strcpy (homeph, optarg);
X break;
X case 'o':
X if (getuid () == 0) {
X oflg++;
X strcpy (slop, optarg);
X break;
X }
X fprintf (stderr, "%s: permission denied\n",
X myname);
X exit (1);
X default:
X usage ();
X }
X }
X if (argc > optind) {
X if (argc > optind + 1)
X usage ();
X
X if (! (pw = getpwnam (argv[optind]))) {
X fprintf (stderr, "%s: unknown user: %s\n",
X myname, argv[optind]);
X exit (1);
X }
X } else {
X if (cp = getlogin ()) {
X if (! (pw = getpwnam (cp))) {
X fprintf (stderr, "%s: unknown user: %s\n",
X myname, cp);
X exit (1);
X }
X } else if (! (pw = getpwuid (getuid ()))) {
X fprintf (stderr, "%s: who are you?\n",
X myname);
X exit (1);
X }
X }
X if (! amroot && pw->pw_uid != getuid ()) {
X fprintf (stderr, "%s: permission denied\n",
X myname);
X exit (1);
X }
X strcpy (user, pw->pw_name);
X printf ("Changing the user information for %s\n", user);
X
X pwent = *pw;
X pwent.pw_name = strdup (pw->pw_name);
X pwent.pw_passwd = strdup (pw->pw_passwd);
X pwent.pw_dir = strdup (pw->pw_dir);
X pwent.pw_shell = strdup (pw->pw_shell);
X
X /*
X * Now get the full name. It is the first comma separated field
X * in the GECOS field.
X */
X
X strcpy (buf, pw->pw_gecos);
X cp = copy_field (buf, fflg ? (char *) 0:fullnm, slop);
X
X /*
X * Now get the room number. It is the next comma separated field,
X * if there is indeed one.
X */
X
X if (cp)
X cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
X
X /*
X * Now get the work phone number. It is the third field.
X */
X
X if (cp)
X cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
X
X /*
X * Now get the home phone number. It is the fourth field.
X */
X
X if (cp)
X cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
X
X /*
X * Anything left over is "slop".
X */
X
X if (cp) {
X if (slop[0])
X strcat (slop, ",");
X
X strcat (slop, cp);
X }
X
X /*
X * If none of the fields were changed from the command line,
X * let the user interactively change them.
X */
X
X if (! fflg && ! rflg && ! wflg && ! hflg && ! oflg)
X new_fields ();
X
X /*
X * Check all of the fields for valid information
X */
X
X valid_field (fullnm, ":,=");
X valid_field (roomno, ":,=");
X valid_field (workph, ":,=");
X valid_field (homeph, ":,=");
X valid_field (slop, ":");
X
X /*
X * Build the new GECOS field by plastering all the pieces together,
X * if they will fit ...
X */
X
X if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
X strlen (homeph) + strlen (slop) > 80) {
X fprintf (stderr, "%s: fields too long\n", myname);
X exit (1);
X }
X sprintf (tmp, "%s,%s,%s,%s", fullnm, roomno, workph, homeph);
X if (slop[0]) {
X strcat (tmp, ",");
X strcat (tmp, slop);
X }
X pwent.pw_gecos = tmp;
X
X /*
X * Now we get to race the bad guy. I don't think he can get us.
X *
X * Ignore most reasonable signals.
X * Maybe we should ignore more? He can't hurt us until the end.
X *
X * Get a lock file.
X *
X * Copy first part of password file to new file.
X * Illegal lines are copied verbatim.
X * File permissions are r--r--r--, owner root, group root.
X *
X * Output the new entry.
X * Only fields in struct passwd are output.
X *
X * Copy the rest of the file verbatim.
X *
X * Rename (link, unlink) password file to backup.
X * Kill me now and nothing changes or no one gets in.
X *
X * Rename (link, unlink) temporary file to password file.
X * Kill me now and no one gets in or lock is left.
X *
X * Remove locking file.
X *
X * That's all folks ...
X */
X
X signal (SIGHUP, SIG_IGN);
X signal (SIGINT, SIG_IGN);
X signal (SIGQUIT, SIG_IGN);
X signal (SIGTERM, SIG_IGN);
X
X ulimit (30000); /* prevent any funny business */
X umask (0); /* get new files modes correct */
X#ifndef NDEBUG
X if ((lockfd = open (".pwdlock", O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
X#else
X if ((lockfd = open (PWDLOCK, O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
X#endif /* NDEBUG */
X {
X puts ("Can't get lock");
X exit (1);
X }
X umask (077); /* close security holes to come ... */
X
X#ifdef DBM
X update_dbm (&pwent);
X#endif
X if (access (NPWDFILE, 0) == 0 && unlink (NPWDFILE) == -1) {
X perror (NPWDFILE);
X exit (1);
X }
X#ifndef NDEBUG
X if ((npwd = fopen ("npasswd", "w")) == (FILE *) 0)
X#else
X umask (077); /* no permissions for non-roots */
X
X if ((npwd = fopen (NPWDFILE, "w")) == (FILE *) 0)
X#endif /* NDEBUG */
X {
X perror (NPWDFILE);
X exit (1);
X }
X#ifndef NDEBUG
X chmod (NPWDFILE, 0444); /* lets have some security here ... */
X chown (NPWDFILE, 0, 0); /* ... and keep the bad guy away */
X#endif /* NDEBUG */
X if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0) {
X perror (NPWDFILE);
X exit (1);
X }
X while (fgets (buf, BUFSIZ, pwd) != (char *) 0) {
X if (buf[0] == '#' || ! (pw = sgetpwent (buf))) {
X fputs (buf, npwd);
X } else if (strcmp (pw->pw_name, pwent.pw_name) != 0)
X fputs (buf, npwd);
X else
X break;
X }
X (void) fprintf (npwd, "%s:", pw->pw_name);
X if (pwent.pw_age)
X (void) fprintf (npwd, "%s,%s:", pwent.pw_passwd, pwent.pw_age);
X else
X (void) fprintf (npwd, "%s:", pwent.pw_passwd);
X
X (void) fprintf (npwd, "%d:%d:%s:%s:%s\n",
X pwent.pw_uid, pwent.pw_gid, pwent.pw_gecos, pwent.pw_dir,
X pwent.pw_shell ? pwent.pw_shell:"");
X
X while (fgets (buf, BUFSIZ, pwd) != (char *) 0)
X fputs (buf, npwd);
X
X if (ferror (npwd)) {
X perror (NPWDFILE);
X if (unlink (NPWDFILE) || unlink (PWDLOCK))
X fputs ("Help!\n", stderr);
X
X exit (1);
X }
X fflush (npwd);
X fclose (npwd);
X#ifdef NDEBUG
X chmod (NPWDFILE, 0644);
X if (unlink (OPWDFILE) == -1) {
X if (errno != ENOENT) {
X puts ("Can't unlink backup file");
X goto unlock;
X }
X }
X if (link (PWDFILE, OPWDFILE) || unlink (PWDFILE)) {
X puts ("Can't save backup file");
X goto unlock;
X }
X#ifndef BSD
X if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE))
X#else
X if (rename (NPWDFILE, PWDFILE))
X#endif
X {
X puts ("Can't rename new file");
X goto unlock;
X }
X#endif /* NDEBUG */
X#ifndef NDEBUG
X (void) unlink (".pwdlock");
X#else
X (void) unlink (PWDLOCK);
X#endif
X exit (0);
X /*NOTREACHED*/
X
Xunlock:
X if (lockfd >= 0)
X (void) unlink (PWDLOCK);
X
X (void) unlink (NPWDFILE);
X exit (1);
X /*NOTREACHED*/
X}
SHAR_EOF
if test 10425 -ne "`wc -c < 'chfn.c'`"
then
echo shar: "error transmitting 'chfn.c'" '(should have been 10425 characters)'
fi
fi
echo shar: "extracting 'chfn.1'" '(1381 characters)'
if test -f 'chfn.1'
then
echo shar: "will not over-write existing file 'chfn.1'"
else
sed 's/^X//' << \SHAR_EOF > 'chfn.1'
X.TH CHFN 1
X.SH NAME
Xchfn \- change user name and information
X.SH SYNOPSIS
X\fBchfn\fR [ \fB-f \fIfull_name\fR ] [ \fB-r \fIroom_no\fR ]
X.br
X[ \fB-w \fIwork_ph\fR ] [ \fB-h \fIhome_ph\fR ] [ \fB-o \fIother\fR ]
X[ \fIuser\fR ]
X.SH DESCRIPTION
X\fIchfn\f changes user fullname, office number, office extension, and home
Xphone number information for a user's account.
XThis information is typically printed by \fIfinger(1)\f and similiar
Xprograms.
XA normal user may only change the fields for their own account,
Xthe super user may change the fields for any account.
XAlso, only the super user may use the \fB-o\f option to change the
Xundefined portions of the GCOS field.
X.PP
XThe only restrictions placed on the contents of the fields is that no
Xcontrol characters may be present, nor any of comma, colon, or equal sign.
XThe \fIother\f field does not have this restriction, and is used to
Xstore accounting information used by other applications.
X.PP
XIf none of the options are selected, \fIchfn\f operates in an interactive
Xfashion, prompting the user with the current values for all of the fields.
XEnter the new value to change the field, or leave the line blank to use
Xthe current value.
XThe current value is displayed between a pair of \fB[ ]\f marks.
XWithout options, chfn prompts for the current user account.
X.SH Files
X/etc/passwd \- user account information
X.SH See Also
Xpasswd(4)
SHAR_EOF
if test 1381 -ne "`wc -c < 'chfn.1'`"
then
echo shar: "error transmitting 'chfn.1'" '(should have been 1381 characters)'
fi
fi
echo shar: "extracting 'pwent.c'" '(6450 characters)'
if test -f 'pwent.c'
then
echo shar: "will not over-write existing file 'pwent.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwent.c'
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 * Duplication is permitted for non-commercial [ profit making ]
X * purposes provided this and other copyright notices remain
X * intact.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <string.h>
X#include "config.h"
X
X#ifdef DBM
X#include <dbm.h>
X#endif
X
X#ifndef lint
Xstatic char _sccsid[] = "@(#)pwent.c 2.2 11:48:02 7/22/90";
X#endif
X
X#define SBUFSIZ 64
X#define NFIELDS 7
X
Xstatic FILE *pwdfp;
Xstatic char pwdbuf[BUFSIZ];
Xstatic char *pwdfile = "/etc/passwd";
X#ifdef DBM
Xstatic int dbmopened;
Xstatic int dbmerror;
X#endif
Xstatic char *pwdfields[NFIELDS];
Xstatic struct passwd pwent;
X
X/*
X * sgetpwent - convert a string to a (struct passwd)
X *
X * sgetpwent() parses a string into the parts required for a password
X * structure. Strict checking is made for the UID and GID fields and
X * presence of the correct number of colons. Any failing tests result
X * in a NULL pointer being returned.
X */
X
Xstruct passwd *sgetpwent (buf)
Xchar *buf;
X{
X int i;
X char *cp;
X
X /*
X * Copy the string to a static buffer so the pointers into
X * the password structure remain valid.
X */
X
X strncpy (pwdbuf, buf, BUFSIZ);
X pwdbuf[BUFSIZ-1] = '\0';
X
X /*
X * Save a pointer to the start of each colon separated
X * field. The fields are converted into NUL terminated strings.
X */
X
X for (cp = pwdbuf, i = 0;i < NFIELDS && cp;i++) {
X pwdfields[i] = cp;
X if (cp = strchr (cp, ':'))
X *cp++ = 0;
X }
X
X /*
X * There must be exactly NFIELDS colon separated fields or
X * the entry is invalid. Also, the UID and GID must be non-blank.
X */
X
X if (i != NFIELDS || *pwdfields[2] == '\0' || *pwdfields[3] == '\0')
X return 0;
X
X /*
X * Each of the fields is converted the appropriate data type
X * and the result assigned to the password structure. If the
X * UID or GID does not convert to an integer value, a NULL
X * pointer is returned.
X */
X
X pwent.pw_name = pwdfields[0];
X pwent.pw_passwd = pwdfields[1];
X if ((pwent.pw_uid = strtol (pwdfields[2], &cp, 10)) == 0 && *cp)
X return 0;
X
X if ((pwent.pw_gid = strtol (pwdfields[3], &cp, 10)) == 0 && *cp)
X return 0;
X
X if (cp = strchr (pwent.pw_passwd, ',')) {
X pwent.pw_age = cp + 1;
X *cp = '\0';
X }
X pwent.pw_gecos = pwdfields[4];
X pwent.pw_dir = pwdfields[5];
X pwent.pw_shell = pwdfields[6];
X
X return (&pwent);
X}
X#ifdef FGETPWENT
X/*
X * fgetpwent - get a password file entry from a stream
X *
X * fgetpwent() reads the next line from a password file formatted stream
X * and returns a pointer to the password structure for that line.
X */
X
Xstruct passwd *fgetpwent (fp)
XFILE *fp;
X{
X char buf[BUFSIZ];
X
X while (fgets (buf, BUFSIZ, fp) != (char *) 0) {
X buf[strlen (buf) - 1] = '\0';
X return (sgetpwent (buf));
X }
X return 0;
X}
X#endif
X#ifdef GETPWENT
X
X/*
X * endpwent - close a password file
X *
X * endpwent() closes the password file if open.
X */
X
Xint endpwent ()
X{
X if (pwdfp)
X if (fclose (pwdfp))
X return -1;
X
X return 0;
X}
X
X/*
X * getpwent - get a password entry from the password file
X *
X * getpwent() opens the password file, if not already opened, and reads
X * a single entry. NULL is returned if any errors are encountered reading
X * the password file.
X */
X
Xstruct passwd *getpwent ()
X{
X if (! pwdfp && setpwent ())
X return 0;
X
X return fgetpwent (pwdfp);
X}
X
X/*
X * getpwuid - locate the password entry for a given UID
X *
X * getpwuid() locates the first password file entry for the given UID.
X * If there is a valid DBM file, the DBM files are queried first for
X * the entry. Otherwise, a linear search is begun of the password file
X * searching for an entry which matches the provided UID.
X */
X
Xstruct passwd *getpwuid (uid)
Xint uid;
X{
X struct passwd *pwd;
X#ifdef DBM
X datum key;
X datum content;
X
X /*
X * Attempt to open the DBM files if they have never been opened
X * and an error has never been returned.
X */
X
X if (! dbmerror && ! dbmopened)
X if (dbminit (pwdfile) == 0)
X dbmopened = 1;
X else
X dbmerror = 1;
X
X /*
X * If the DBM file are now open, create a key for this UID and
X * try to fetch the entry from the database. A matching record
X * will be unpacked into a static structure and returned to
X * the user.
X */
X
X if (dbmopened) {
X pwent.pw_uid = uid;
X key.dsize = sizeof pwent.pw_uid;
X key.dptr = (char *) &pwent.pw_uid;
X content = fetch (key);
X if (content.dptr != 0) {
X memcpy (pwdbuf, content.dptr, content.dsize);
X pw_unpack (pwdbuf, content.dsize, &pwent);
X return &pwent;
X }
X }
X#endif
X /*
X * Rewind the database and begin searching for an entry which
X * matches the UID. Return the entry when a match is found.
X */
X
X if (setpwent ())
X return 0;
X
X while (pwd = getpwent ())
X if (pwd->pw_uid == uid)
X return pwd;
X
X return 0;
X}
X
Xstruct passwd *getpwnam (name)
Xchar *name;
X{
X struct passwd *pwd;
X#ifdef DBM
X datum key;
X datum content;
X
X /*
X * Attempt to open the DBM files if they have never been opened
X * and an error has never been returned.
X */
X
X if (! dbmerror && ! dbmopened)
X if (dbminit (pwdfile) == 0)
X dbmopened = 1;
X else
X dbmerror = 1;
X
X /*
X * If the DBM file are now open, create a key for this UID and
X * try to fetch the entry from the database. A matching record
X * will be unpacked into a static structure and returned to
X * the user.
X */
X
X if (dbmopened) {
X key.dsize = strlen (name);
X key.dptr = name;
X content = fetch (key);
X if (content.dptr != 0) {
X memcpy (pwdbuf, content.dptr, content.dsize);
X pw_unpack (pwdbuf, content.dsize, &pwent);
X return &pwent;
X }
X }
X#endif
X /*
X * Rewind the database and begin searching for an entry which
X * matches the name. Return the entry when a match is found.
X */
X
X if (setpwent ())
X return 0;
X
X while (pwd = getpwent ())
X if (strcmp (pwd->pw_name, name) == 0)
X return pwd;
X
X return 0;
X}
X
X/*
X * setpwent - open the password file
X *
X * setpwent() opens the system password file, and the DBM password files
X * if they are present. The system password file is rewound if it was
X * open already.
X */
X
Xint setpwent ()
X{
X if (! pwdfp) {
X if (! (pwdfp = fopen (pwdfile, "r")))
X return -1;
X } else {
X if (fseek (pwdfp, 0L, 0) != 0)
X return -1;
X }
X#ifdef DBM
X /*
X * Attempt to open the DBM files if they have never been opened
X * and an error has never been returned.
X */
X
X if (! dbmerror && ! dbmopened)
X if (dbminit (pwdfile) == 0)
X dbmopened = 1;
X else
X dbmerror = 1;
X#endif
X return 0;
X}
X#endif
SHAR_EOF
if test 6450 -ne "`wc -c < 'pwent.c'`"
then
echo shar: "error transmitting 'pwent.c'" '(should have been 6450 characters)'
fi
fi
echo shar: "extracting 'pwpack.c'" '(1815 characters)'
if test -f 'pwpack.c'
then
echo shar: "will not over-write existing file 'pwpack.c'"
else
sed 's/^X//' << \SHAR_EOF > 'pwpack.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 * Duplication is permitted for non-commercial [ profit making ]
X * purposes provided this and other copyright notices remain
X * intact.
X */
X
X#include <pwd.h>
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)pwpack.c 2.1 14:30:43 7/22/90";
X#endif
X
Xint pw_pack (passwd, buf)
Xstruct passwd *passwd;
Xchar *buf;
X{
X char *cp;
X
X cp = buf;
X strcpy (cp, passwd->pw_name);
X cp += strlen (cp) + 1;
X
X strcpy (cp, passwd->pw_passwd);
X cp += strlen (cp) + 1;
X
X memcpy (cp, (void *) &passwd->pw_uid, sizeof passwd->pw_uid);
X cp += sizeof passwd->pw_uid;
X
X memcpy (cp, (void *) &passwd->pw_gid, sizeof passwd->pw_gid);
X cp += sizeof passwd->pw_gid;
X
X strcpy (cp, passwd->pw_gecos);
X cp += strlen (cp) + 1;
X
X strcpy (cp, passwd->pw_dir);
X cp += strlen (cp) + 1;
X
X strcpy (cp, passwd->pw_shell);
X cp += strlen (cp) + 1;
X
X return cp - buf;
X}
X
Xint pw_unpack (buf, len, passwd)
Xchar *buf;
Xint len;
Xstruct passwd *passwd;
X{
X char *org = buf;
X
X passwd->pw_name = buf;
X buf += strlen (buf) + 1;
X if (buf - org > len)
X return -1;
X
X passwd->pw_passwd = buf;
X buf += strlen (buf) + 1;
X if (buf - org > len)
X return -1;
X
X memcpy ((void *) &passwd->pw_uid, (void *) buf, sizeof passwd->pw_uid);
X buf += sizeof passwd->pw_uid;
X if (buf - org > len)
X return -1;
X
X memcpy ((void *) &passwd->pw_gid, (void *) buf, sizeof passwd->pw_gid);
X buf += sizeof passwd->pw_gid;
X if (buf - org > len)
X return -1;
X
X passwd->pw_gecos = buf;
X buf += strlen (buf) + 1;
X if (buf - org > len)
X return -1;
X
X passwd->pw_dir = buf;
X buf += strlen (buf) + 1;
X if (buf - org > len)
X return -1;
X
X passwd->pw_shell = buf;
X buf += strlen (buf) + 1;
X if (buf - org > len)
X return -1;
X
X return 0;
X}
SHAR_EOF
if test 1815 -ne "`wc -c < 'pwpack.c'`"
then
echo shar: "error transmitting 'pwpack.c'" '(should have been 1815 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
More information about the Alt.sources
mailing list