Path: seismo!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: chsh,chfn - Original contained security bugs. Message-ID: <1362@panda.UUCP> Date: 31 Jan 86 22:30:59 GMT Sender: jpn@panda.UUCP Lines: 388 Approved: jpn@panda.UUCP Mod.sources: Volume 3, Issue 99 Submitted by: seismo!mcvax!htsa!jack (Jack Jansen) [ moderator's note: I have had SEVERAL people send mail indicating security problems with the original chsh,chfn. This is the only mail I recieve that contained code fixes. I have not tried to verify that THIS version is secure - but it appears to be MUCH better than the original. John Nelson, moderator ] : 'This is a shell archive. Run with the real shell,' : 'not the seashell. It should extract the following:' : ' READ_ME Makefile chfn.1 chsh.1 chsh.c ' echo x - READ_ME sed 's/^X//' <<'EndOfFile' >READ_ME 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. EndOfFile echo x - Makefile sed 's/^X//' <<'EndOfFile' >Makefile XCFLAGS=-O XDEST=/usr/local XDOC_DEST=/usr/man/man9 X Xchfn chsh: chsh.o X cc $(CFLAGS) chsh.o -o chsh X -rm chfn X ln chsh chfn 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 cp chsh.1 $(DOC_DEST)/chsh.1 X cp chfn.1 $(DOC_DEST)/chfn.1 EndOfFile echo x - chfn.1 sed 's/^X//' <<'EndOfFile' >chfn.1 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. EndOfFile echo x - chsh.1 sed 's/^X//' <<'EndOfFile' >chsh.1 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. EndOfFile echo x - chsh.c sed 's/^X//' <<'EndOfFile' >chsh.c 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 1 /* Define this if you don't have putpwent */ X#define SECURE 1 /* Only owner/root can change stuff */ X#define DEBUG 1 /* ALWAYS DEFINE THIS AT FIRST */ X#define void int /* If your compiler doesn't know void */ X X#include X#include X#include X#include 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(), *mktemp(); Xstruct passwd *getpwent(), *getpwnam(), *fgetpwent(); Xchar *index(); 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( index(Arg, '\n') || index(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 (!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 EndOfFile exit