[comp.sys.ncr] chsh broken

bt455s10@uhccux.uhcc.hawaii.edu (Carl "Art" McIntosh) (01/17/90)

The commands chsh and chfn in /usr/ucb under OS 2.01.00 seem broken,
at least they don't behave as expected :^)).  The problem appears to
be that if you are root, and execute chsh, the program exits with
the following error message:

# chsh
Changing login shell for root
Old shell: /bin/sh
New shell: /bin/sh	<=== I type this
Sorry, password file changedsize: 1347, expected 1378
Warn your system administrator, please.
#

Upon examination of the /etc/ptmp.bad file which chsh creates,
I see that the shell for *every* account with UID 0 has been
changed to /bin/sh.  This of course includes sa, startup and
shutdown.  The chsh NCR is using seems to behave awfully
similar to the one posted to the net a while back, I remember
installing the program on my ISC unix system at home and 
fixing the *very same* problem in it.  BTW, NCR seems to 
include PD utilities along with their OS and not acknowlege
such, for instance the /etc/zoneinfo junk looks alot like
the tz package posted to the net by ado@ncifcrf.gov ....

Below is the chsh/chfn package from the net I mentioned above,
already fixed to check the user's *name* instead of the UID,
so the program behaves as expected.  chfn is really just a
link to chsh so it suffers from the same malady as chsh.

----------------------- cut here -------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile READ_ME chfn.1 chsh.1 chsh.c
# Wrapped by root@pilikia on Tue Jan 16 18:46:38 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(451 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XCFLAGS=-O
XDEST=/usr/local/bin
XDOC_DEST=/usr/catman/u_man/man9
X
Xchfn chsh: chsh.o
X	cc $(CFLAGS) chsh.o -o chsh
X	-rm chfn
X	ln chsh chfn
X
Xclean:
X	rm -f chsh chfn *.o core
X
Xinstall: chsh chfn
X	@echo Warning: you must be superuser to do this.
X	-rm $(DEST)/chsh $(DEST)/chfn
X	cp chsh $(DEST)/chsh
X	ln $(DEST)/chsh $(DEST)/chfn
X	chown root $(DEST)/chsh
X	chmod 4711 $(DEST)/chsh
X	nroff -man chsh.1 > $(DOC_DEST)/chsh.1
X	nroff -man chfn.1 > $(DOC_DEST)/chfn.1
END_OF_FILE
if test 451 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'READ_ME' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'READ_ME'\"
else
echo shar: Extracting \"'READ_ME'\" \(1567 characters\)
sed "s/^X//" >'READ_ME' <<'END_OF_FILE'
XThis program was originally written by K. Richard Magill,
Xand posted to mod.sources. Since it contained a few
XSYS5 dependencies, a *serious* security bug, and no sanity
Xchecks at all, I decided to hack it up a bit.
X
XIt now checks a given shell for existence and executability (only
Xby looking at the mode, sorry), and it honors the standard
X(as far as I know) algorithm for locking password files.
X
XThe serious security bug was the following: Imagine a user called
Xhacky doing the following:
Xchsh hacky '/bin/sh
Xdummy::0:0::/:'
X
XSo, if you've installed it already, better remove it *fast*.
X
XAlso, some cosmetic changes were made: If no username is given, the
Xcurrent user is assumed, and if no shell/realname is given, the
Xold one is printed, and a new one asked.
X
XINSTALLATION:
X
XFirst, look at the defines at the top in chsh.c. If your system has
Xputpwent(), remove the #define NOPUTPWENT.
X
XSecond, if you don't mind people playing with other people's
Xshells and names, remove the #define SECURE.
X
XThird, KEEP YOUR HANDS OFF the #define DEBUG.
X
XI tried this on a mucho hacked up 11/34 running V7, so it is not
Xmore than sensible that you test it before letting it anywhere near
Xthe password file.
X
XNow, compile it, run it a few times (not as super-user), and,
Xwhen you're satisfied, remove the #define DEBUG.
X
XNow you can type make install, to re-compile and install it.
X(Don't forget to look at the Makefile to make sure all
Xpaths are as you would like them).
X
X--
X	Jack Jansen, jack@htsa.UUCP (or jack@mcvax.UUCP)
X	...!mcvax!vu44!htsa!jack
X	The shell is my oyster.
END_OF_FILE
if test 1567 -ne `wc -c <'READ_ME'`; then
    echo shar: \"'READ_ME'\" unpacked with wrong size!
fi
# end of 'READ_ME'
fi
if test -f 'chfn.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'chfn.1'\"
else
echo shar: Extracting \"'chfn.1'\" \(829 characters\)
sed "s/^X//" >'chfn.1' <<'END_OF_FILE'
X.TH CHFN 1 Local
X.SH NAME
Xchfn \- change user's real name
X.SH SYNOPSIS
X.B chfn
X[ user [ realname ] ]
X.SH DESCRIPTION
X.I chfn
Xallows the user to change her real name, as printed by
X.I finger(1)
Xand
X.I who(1).
XIf no
X.B user
Xargument is given, the real name is changed for the person
Xcurrently logged in.
X.PP
XIf no
X.B realname
Xis given, the current name is printed, and a new one is asked.
X.PP
XDepending on choices made by the system administrator, it might
Xor might not be possible to modify someone else's name. The program
Xwill then ask for the correct password first.
X.SH SEE ALSO
Xchsh(1), finger(1), who(1)
X.SH DIAGNOSTICS
XAll kinds of problems with the password file, and locking it,
Xare reported, and the program exits.
X.SH AUTHOR
XK. Richard Magill, rich@rexaco1.UUCP
X.br
XExtensively modified by Jack Jansen, jack@htsa.UUCP.
END_OF_FILE
if test 829 -ne `wc -c <'chfn.1'`; then
    echo shar: \"'chfn.1'\" unpacked with wrong size!
fi
# end of 'chfn.1'
fi
if test -f 'chsh.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'chsh.1'\"
else
echo shar: Extracting \"'chsh.1'\" \(982 characters\)
sed "s/^X//" >'chsh.1' <<'END_OF_FILE'
X.TH CHSH 1 Local
X.SH NAME
Xchsh \- change login shell
X.SH SYNOPSIS
X.B chsh
X[ user [ shell ] ]
X.SH DESCRIPTION
X.I chsh
Xallows the user to change her login shell.
XIf no
X.B user
Xargument is given, the login shell is changed for the person
Xcurrently logged in.
X.PP
XIf no
X.B shell
Xis given, the current shell is printed, and a new one is asked.
X.PP
XDepending on choices made by the system administrator, it might
Xor might not be possible to modify someone else's shell. The program
Xwill then ask for the correct password first.
X.SH SEE ALSO
Xchfn(1), login(1)
X.SH DIAGNOSTICS
XThe
X.B shell
Xgiven is checked for existence, and executability.
X.br
XAlso, all kinds of problems with the password file, and locking it,
Xare reported, and the program exits.
X.SH BUGS
XThe executability check only looks at the mode, so it doesn't
Xguarantee that you will be able to log in with the given shell.
X.SH AUTHOR
XK. Richard Magill, rich@rexaco1.UUCP
X.br
XExtensively modified by Jack Jansen, jack@htsa.UUCP.
END_OF_FILE
if test 982 -ne `wc -c <'chsh.1'`; then
    echo shar: \"'chsh.1'\" unpacked with wrong size!
fi
# end of 'chsh.1'
fi
if test -f 'chsh.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'chsh.c'\"
else
echo shar: Extracting \"'chsh.c'\" \(6000 characters\)
sed "s/^X//" >'chsh.c' <<'END_OF_FILE'
X/*
X *	This program was originally written by K. Richard Magill,
X *	and posted to mod.sources. It has been extensively
X *	modified by Jack Jansen. See below for details.
X *
X *	K. Richard Magill, 26-jan-86.
X *	Last Mod 26-jan-86, rich.
X *	Modified by Jack Jansen, 30-jan-86:
X *	- It now runs under V7.
X *	- It now uses (what I believe to be) standard
X *	  password file locking and backups.
X *	- Check the size of the new passwd file, abort if
X *	  it looks funny.
X *	- Check that there are no :colons: or \nnewlines\n in the
X *	  given string.
X *	- Use name from getlogin() if not given, and ask for
X *	  parameters if not given.
X *	- if SECURE is defined, don't let other people
X *	  muck finger/shell info.
X */
X/* #define NOPUTPWENT		/* Define this if you don't have putpwent */
X#define SECURE			/* Only owner/root can change stuff */
X/* #define DEBUG		/* ALWAYS DEFINE THIS AT FIRST */
X/*#define void	int		/* If your compiler doesn't know void */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <pwd.h>
X
X#define WATCH(s,x)	if(x){perror(s);return(-1);}
X
Xchar *PASSWD = "/etc/passwd";
X#ifndef DEBUG
Xchar *BACKUP = "/etc/passwd.bak";
Xchar *LOCK = "/etc/vipw.lock";
Xchar *TEMP = "/etc/ptmp";
Xchar *BAD_TEMP = "/etc/ptmp.bad";
X#else
Xchar *LOCK = "vipw.lock";
Xchar *TEMP = "ptmp";
Xchar *BAD_TEMP = "ptmp.bad";
X#endif /* DEBUG */
Xchar ArgBuf[128];
Xchar *Arg = ArgBuf;
X
Xvoid endpwent(), perror();
Xchar *crypt(), *getpass(), *getlogin(), *mktemp();
Xstruct passwd *getpwent(), *getpwnam(), *fgetpwent();
Xchar *strchr();
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	register int i;
X	register struct passwd *p;
X	FILE *fout;
X	int target_id;			/* Who are we changing? */
X	struct stat stat_buf;
X	long  OldLen, NewLen;		/* Old/New length of passwd */
X	long LenDiff;			/* Expected length dif. */
X	int ShellMode;			/* True if chsh */
X	char *UserName;			/* Who are we working for */
X
X	if( strcmp(argv[0], "chsh") == 0 ) ShellMode = 1; else
X	if( strcmp(argv[0], "chfn") != 0 ) {
X	    fprintf(stderr,"Sorry, program name should be 'chsh' or 'chfn'.\n");
X	    exit(1);
X	}
X
X	if( argc >= 2 ) {	/* Login name given */
X		UserName = argv[1];
X	} else {
X		UserName = getlogin();
X		printf("Changing %s for %s\n", ShellMode ? "login shell":
X			"real name", UserName);
X	}
X
X	/* is this person real? */
X
X	if ((p = getpwnam(UserName)) == NULL) {
X		(void) fprintf(stderr, "%s: don't know %s\n",
X			argv[0], UserName);
X		return(-1);
X	}	/* if person isn't real */
X
X	/* do we have permission to do this? */
X	target_id = p->pw_uid;
X
X	if ((i = getuid()) != 0 && i != target_id) {
X#ifdef SECURE
X		fprintf(stderr,"Sorry, you don't have permission to do that.\n");
X		exit(1);
X#else
X		char salt[3];
X
X		salt[0] = p->pw_passwd[0];
X		salt[1] = p->pw_passwd[1];
X		salt[3] = '\0';
X
X		if (*p->pw_passwd != '\0'
X			&& strncmp(crypt(getpass("Password: "), salt),
X			p->pw_passwd, 8)) {
X			(void) fprintf(stderr, "Sorry.\n");
X			return(-1);
X		}	/* passwd didn't match */
X#endif /* SECURE */
X	}	/* check for permission */
X
X	/* If in verbose mode, print old info */
X	if( argc <= 2 ) {
X	    if( ShellMode ) {
X		printf("Old shell: %s\n", p->pw_shell?p->pw_shell:"");
X		printf("New shell: ");
X		gets(Arg);
X	    } else {
X		printf("Old name: %s\n", p->pw_gecos?p->pw_gecos:"");
X		printf("New name: ");
X		gets(Arg);
X	    }
X	} else {
X	    Arg = argv[2];
X	}
X
X	/* Check for dirty characters */
X	if( strchr(Arg, '\n') || strchr(Arg, ':') ) {
X	    fprintf(stderr,"%s: Dirty characters in argument.\n",argv[0]);
X	    exit(1);
X	}
X
X	/* Check that the shell sounds reasonable */
X	if( ShellMode ) {
X	    if( *Arg != '/' ) {
X		fprintf(stderr,"%s: shell name should be full path.\n",Arg);
X		exit(1);
X	    }
X	    WATCH(Arg,stat(Arg,&stat_buf));
X	    if( (stat_buf.st_mode & 0111) == 0 ) {
X		fprintf(stderr,"%s is not an executable.\n");
X		exit(1);
X	    }
X	}
X
X	/* set up files */
X
X	endpwent();	/* close passwd file */
X
X	setpwent();
X
X	/* Now, lock the password file */
X	creat(LOCK,0600);	/* This might fail. No problem */
X	if( link(LOCK,TEMP) < 0 ) {
X		fprintf(stderr,"Sorry, password file busy.\n");
X		exit(1);
X	}
X	WATCH(TEMP,(fout = fopen(TEMP, "w")) == NULL);
X
X	while ((p = getpwent()) != NULL) {
X		/*if (p->pw_uid == target_id) {*/
X		if (strcmp(p->pw_name, UserName) == 0) {
X			if (!ShellMode ) {
X				LenDiff = strlen(Arg)-strlen(p->pw_gecos);
X				p->pw_gecos = Arg;
X			} else {
X				LenDiff = (-strlen(p->pw_shell));
X				p->pw_shell = Arg == NULL ? "/bin/sh"
X					: Arg;
X				LenDiff += strlen(p->pw_shell);
X			}
X		}	/* if this is entry to be changed */
X
X		WATCH("putpwent",putpwent(p, fout));
X	}	/* while not eof (we couldn't recognize an error) */
X
X	/* close files */
X	endpwent();
X	fclose(fout);
X
X	/* Check that sizes are as expected */
X	WATCH(TEMP, stat(TEMP, &stat_buf) );
X	NewLen = stat_buf.st_size;
X	WATCH(PASSWD, stat(PASSWD, &stat_buf) );
X	OldLen = stat_buf.st_size;
X	if( OldLen + LenDiff != NewLen ) {
X	    fprintf(stderr,"Sorry, password file changed size: %ld, expected %ld.\n", NewLen, OldLen+LenDiff);
X	    fprintf(stderr,"Warn your system administrator, please.\n");
X	    WATCH(TEMP, link(TEMP,BAD_TEMP));
X	    WATCH(TEMP,unlink(TEMP));
X	    WATCH(LOCK,unlink(LOCK));
X	    exit(1);
X	}
X
X#ifndef DEBUG
X	/* remove old backup if it exists */
X	WATCH(BACKUP,!stat(BACKUP, &stat_buf) && unlink(BACKUP));
X
X	/* make current passwd file backup */
X	WATCH("linking passwd to passwd.bak",link(PASSWD, BACKUP) || unlink(PASSWD));
X
X	/* make new file passwd */
X	WATCH("linking temp to passwd",link(TEMP, PASSWD) || unlink(TEMP));
X	WATCH("chmod passwd", chmod(PASSWD, 0644));
X#endif /* DEBUG */
X
X	/* Remove lock */
X	WATCH(LOCK,unlink(LOCK));
X
X#ifdef DEBUG
X	printf("Now, check that %s looks reasonable.\n", TEMP);
X#endif /* DEBUG */
X	/* must have succeeded */
X	return(0);
X}	/* main */
X
X#ifdef NOPUTPWENT
Xputpwent(ent, fp)
X    FILE *fp;
X    struct passwd *ent;
X    {
X
X    fprintf(fp,"%s:%s:%d:%d:%s:%s:%s\n", ent->pw_name, ent->pw_passwd,
X	ent->pw_uid, ent->pw_gid, ent->pw_gecos, ent->pw_dir,
X	ent->pw_shell);
X    return(0);
X}
X#endif /* NOPUTPWENT */
END_OF_FILE
if test 6000 -ne `wc -c <'chsh.c'`; then
    echo shar: \"'chsh.c'\" unpacked with wrong size!
fi
# end of 'chsh.c'
fi
echo shar: End of shell archive.
exit 0
----------------------- cut here -------------------------
-- 

Art Neilson		    | ARPA: manapua!pilikia!root@trout.nosc.mil
Bank of Hawaii Tech Support | UUCP: uunet!ucsd!nosc!manapua!pilikia!root

wescott@Columbia.NCR.COM (Mike Wescott) (01/19/90)

In article <6186@uhccux.uhcc.hawaii.edu> bt455s10@uhccux.uhcc.hawaii.edu (Carl "Art" McIntosh) writes:
> The commands chsh and chfn in /usr/ucb under OS 2.01.00 seem broken,

Yep. It is..

> The chsh NCR is using seems to behave awfully
> similar to the one posted to the net a while back

It's the same one.

> the /etc/zoneinfo junk looks alot like
> the tz package posted to the net by ado@ncifcrf.gov ....

Again, we used ado's software.  However, we have not deliberately
slighted anyone.  It is difficult to introduce PD software into the
product and to make sure that proper credit appears in the documentation.
We could do a better job there.

> Below is the chsh/chfn package from the net I mentioned above,
> already fixed to check the user's *name* instead of the UID,

Thanks, I'll see that your fix gets implemented.

--
	-Mike Wescott
	 mike.wescott@ncrcae.Columbia.NCR.COM

wescott@Columbia.NCR.COM (Mike Wescott) (01/19/90)

In article <1904@sauron.Columbia.NCR.COM> wescott@micky.Columbia.NCR.COM (Mike Wescott) writes:
> In article <6186@uhccux.uhcc.hawaii.edu> bt455s10@uhccux.uhcc.hawaii.edu (Carl "Art" McIntosh) writes:
> > The commands chsh and chfn in /usr/ucb under OS 2.01.00 seem broken,
> 
> Yep. It is..

I spoke too quickly.  Yes it is broken in OS 2.01.00 but fixed in a later
release.

--
	-Mike Wescott
	 mike.wescott@ncrcae.Columbia.NCR.COM