[comp.sources.misc] v05i017: portable

greywolf@unisoft.UUCP (The Grey Wolf) (10/28/88)

Posting-number: Volume 5, Issue 17
Submitted-by: "The Grey Wolf" <greywolf@unisoft.UUCP>
Archive-name: vipw

[Some comments:

(1) some shells break or slow down when the leading character on lines in
    a here document is the same as the leading character of the "EOF word".
    This has got that bug!  (I fixed it, just in case.)

(2) The code is in "debug mode"; rearrange the comments a bit to make a
    working version.

(3) I hate to tell you, greywolf, but this thing is just as useful under
    Xenix or System V; neither of which are guaranteed to mount /usr.
    Stupid parochialisms are why this critter's needed in the first place!

++bsa]

After seeing the questions on comp.unix.questions about a vipw program,
I thought I would submit one.  It seems to me to be completely portable;
if not, the tweaks required are likely minimal.  It includes sanity
checking for a uid 0 entry (verification of shell, home directory, password);
and produces appropriate messages explaining what happened.
Enjoy.  Comments?  Flames?  -> ...!{sun,ucbvax,well,uunet}!unisoft!greywolf
-----cut here----------cut here----------cut here----------cut here-----
#!/bin/sh -
# This is a shell archive, meaning:  
#	1: delete everything above the #! /bin/sh - line.
#	2: use sh (NOT csh) to unpack the archive.
#
# This file contains:
#	vipw.sh (3832 bytes) 
#
# Packed by greywolf@unisoft.UniSoft (The Grey Wolf) 30 September 1988
sed 's/^FOO_//g' << '_BAR_' > vipw.sh
FOO_#! /bin/sh -
FOO_# vipw: edit passwd file with locking.
FOO_# unicom!greywolf
FOO_
FOO_# PATH and IFS are here for paranoia's sake
FOO_# The path may be modified as necessary.
FOO_IFS=' 	
FOO_'
FOO_PATH=/etc:/bin:/usr/bin:/usr/ucb:/usr/etc:/usr/local/bin export PATH IFS
FOO_
FOO_EDITOR=${EDITOR-vi}
FOO_
FOO_# PASSWD=/etc/passwd PTMP=/etc/ptmp
FOO_PASSWD=./passwd PTMP=./ptmp
FOO_
FOO_# if the temp file exists, tell 'em to come back later.
FOO_
FOO_if [ -f $PTMP ]
FOO_then	echo "vipw: temp file busy, try again later."
FOO_	exit 0
FOO_fi
FOO_
FOO_# simplify cleanup
FOO_trap "/bin/rm -f $PTMP ; exit 0" 0 1 2 3 15
FOO_
FOO_cp $PASSWD $PTMP		# so we don't have the real thing.
FOO_if chmod 600 $PTMP		# This could be something like 644...
FOO_then	:
FOO_else	exit $?
FOO_fi
FOO_
FOO_# check if /usr is mounted; if we're single-user, it likely isn't, at least
FOO_# on any SANE implementation of a hierarchy.  (Sun 4.0 is not a sane hier-
FOO_# archy in my opinion.)
FOO_
FOO_if mount | grep "/usr " > /dev/null
FOO_then	mount=0
FOO_else	mount=1			# set a flag to umount /usr later
FOO_	if mount /usr
FOO_	then  :
FOO_	else  echo "mounting /usr failed"
FOO_	      exit $?	# if it failed, we have problems -- better leave
FOO_	fi
FOO_fi
FOO_
FOO_# edit the thing.
FOO_
FOO_$EDITOR $PTMP
FOO_
FOO_# was it changed?  don't shuffle files unnecessarily.
FOO_
FOO_if cmp -s $PTMP $PASSWD
FOO_then 	echo "No changes appear to have been made."
FOO_	exit 0
FOO_fi
FOO_
FOO_#
FOO_# check the super-user password entry.  There MUST be at least one user with
FOO_# uid 0.  If not, then we exit.  If the password is not of the 13 char
FOO_# length, then it cannot be decrypted, so we exit.  If the password has an
FOO_# asterisk in it, it cannot be decrypted, so we exit.  If the ptmp file gets
FOO_# deleted/trashed, we don't even bother moving it.  If root's login shell is
FOO_# not executable, then we don't allow it, and we exit.  If root's passwd is
FOO_# zero-length, then we issue a warning only.
FOO_# Replacing root's (bad) shell with /bin/sh is tricky without another tmp
FOO_# file, and that's getting sloppy.
FOO_
FOO_# we don't demand "root" as the first uid 0 entry.  I use "wizard" on some
FOO_# machines.
FOO_
FOO_echo "verifying superuser password entry..."
FOO_
FOO_# length of a minimal superuser password entry when echoed portably is
FOO_# 14 chars:
FOO_# "root::0:0::/:" + the newline that echo prints.
FOO_
FOO_if [ `cat $PTMP | wc -c` -lt 14 ]
FOO_then	echo "panic: zero-length file!
FOO_passwd file unchanged."
FOO_	exit 1
FOO_fi
FOO_
FOO_# look for a uid 0 entry -- this MUST be first on the line or this will fail.
FOO_if pwd=`head -1 $PTMP | egrep '^[^:]*:[^:]*:0:[0-9^:]*:[^:]*:[^:]*:.*[^:]$'`
FOO_then	pwd="`echo $pwd | 
FOO_	      sed -e 's/::/:NULL:/g' \
FOO_	      -e 's/:.*\*[^:]*:/:STAR:/g' -e 's,:$,:/bin/sh,'`"
FOO_else	echo "panic: basic super-user entry missing
FOO_passwd file unchanged."
FOO_	exit 1
FOO_fi
FOO_
FOO_# split the root entry for easy access
FOO_set `echo "$pwd" | 
FOO_     awk -F: '{ printf "%s %s %d %d %s %s",$1,$2,$3,$4,$6,$7 }'`
FOO_
FOO_pw_name="$1" pw_passwd="$2" pw_uid="$3" pw_gid="$4"
FOO_pw_dir="$5" pw_shell="$6"
FOO_
FOO_
FOO_not_ok=0		# everything is ok until we find otherwise
FOO_
FOO_# The length of a password will be 13 chars; when echoed portably it will be
FOO_# 14 chars due to the newline returned by echo
FOO_
FOO_if [ `echo "$pw_passwd" | wc -c` -lt 14 ]
FOO_then	if [ "$pw_passwd" = "NULL" ]
FOO_	then	echo "warning: null super-user passwd."
FOO_		not_ok=1
FOO_	else
FOO_		echo "panic: impossible super-user password.
FOO_passwd file unchanged."
FOO_		exit 1
FOO_	fi
FOO_fi
FOO_
FOO_# root MUST log into / as home directory.  Why?  Enforcement of convention.
FOO_# if you don't like it, change it.
FOO_
FOO_if [ ! "$pw_dir" = "/" ]
FOO_then	echo "panic: bad super-user directory.
FOO_passwd file unchanged."
FOO_	exit 1
FOO_fi
FOO_
FOO_# We have to have an existing executable shell, or else root's login/su
FOO_# attempts will fail miserably.
FOO_# Why doesn't test have a -x option?!?
FOO_
FOO_if [ ! -f "$pw_shell" ]
FOO_then	echo "panic: can't find shell $pw_shell!
FOO_passwd file unchanged."
FOO_	exit 1
FOO_fi
FOO_
FOO_if [ $not_ok -eq 0 ] 
FOO_then	echo ok.
FOO_fi
FOO_
FOO_# make passwd file read-only to discourage direct use of an editor on the
FOO_# file.
FOO_chmod 444 $PTMP
FOO_mv -f $PTMP $PASSWD
FOO_echo done.
FOO_exit 0
_BAR_
if [ `cat vipw.sh` -ne 3832 ]
then
    echo "vipw.sh unpacked with wrong size -- should be 3832 bytes
fi
# end of shar file.