Xref: neuron alt.sources:1930 comp.editors:1852 Path: neuron!news.sni.de!Germany.EU.net!mcsun!uknet!doc.ic.ac.uk!agate!usenet.ins.cwru.edu!gatech!news-feed-1.peachnet.edu!bogus.sura.net!darwin.sura.net!zaphod.mps.ohio-state.edu!caen!nic.umass.edu!noc.near.net!das-news.harvard.edu!ogicse!usenet.ee.pdx.edu!pdxgate!kirkenda@rigel.cs.pdx.edu From: kirkenda@rigel.cs.pdx.edu (Steve Kirkendall) Newsgroups: alt.sources,comp.editors Subject: Security hole in elvis 1.7 (fix) Message-ID: <7543@pdxgate.UUCP> Date: 21 May 93 18:46:17 GMT Article-I.D.: pdxgate.7543 Sender: news@pdxgate.UUCP Organization: Portland State University, Computer Science Dept. Lines: 287 The following patches affect the "elvprsv" program under UNIX and VMS. ("elvprsv" is Elvis 1.7's file preservation program.) There is a security hole in the elvis' "elvprsv" command, when run under UNIX. The patches below fix it. That's the main reason for this post. But as long as I'm posting anyway, I might as well take a stab at file recovery under VMS. I wasn't aware until a few days ago that the VMS version of elvis didn't support file recovery. I'm no VMS guru, and I don't even have access to a VAX on which to test the following, but if you're desperate the patches below *might* make "elvprsv" usable under VMS. The "elvrec" program still hasn't been ported, so you'll want to run "elvprsv -R tmpfilename" to recover your text directly. To compile elvprsv, try running the following commands: cc elvprsv.c link/exe=elvprsv.exe elvprsv,sys$input.opt Again, let me caution the VMS crowd that this might not work! I don't really know my way around VMS; I'm just plucking significant-looking commands and code from John Campbell's stuff, and crossing my fingers. Assuming it does compile correctly, you might want to try it out first on an unimportant file. Let me know what happens. ------------------------------------------------------------------------ Steve Kirkendall, kirkenda@cs.pdx.edu, Grad student at Portland State U. # --------------------------- cut here ---------------------------- # This is a shar archive. To unpack it, save it to a file, and delete # anything above the "cut here" line. Then run sh on the file. # # -rw-r--r-- 1 steve group 6052 May 21 11:00 elv17pl1 # if test -f elv17pl1 -a "$1" != -f then echo Will not overwrite elv17pl1 else echo Extracting elv17pl1 sed 's/^X//' >elv17pl1 <<\eof X*** ../1.7/elvprsv.c Fri May 14 11:39:34 1993 X--- elvprsv.c Fri May 21 10:22:54 1993 X*************** X*** 84,89 **** X--- 84,93 ---- X # include "wildcard.c" X #endif X X+ #if VMS X+ char *ownername(file) char *file; { return "user"; } X+ void mail(user, file, when) {}; X+ #endif X X BLK buf; X BLK hdr; X*************** X*** 281,287 **** X argv = wildexpand(&argc, argv); X #endif X X! /* do we have a "-c", "-R", or "-when elvis died" argument? */ X i = 1; X if (argc >= i + 1 && !strcmp(argv[i], "-R")) X { X--- 285,291 ---- X argv = wildexpand(&argc, argv); X #endif X X! /* do we have a "-R", or "-when elvis died" argument? */ X i = 1; X if (argc >= i + 1 && !strcmp(argv[i], "-R")) X { X*************** X*** 303,308 **** X--- 307,321 ---- X when = argv[i] + 1; X i++; X } X+ X+ #if VMS X+ /* VMS doesn't have `elvrec', so `elvprsv' should always use `-R' */ X+ if (!rewrite_now) X+ { X+ fprintf(stderr, "VMS doesn't have `elvrec', so `elvprsv' should always use `-R'\n"); X+ exit(0); X+ } X+ #endif X X /* preserve everything we're supposed to */ X while (i < argc) X*** ../1.7/prsvunix.c Fri May 14 11:39:56 1993 X--- prsvunix.c Fri May 21 10:46:45 1993 X*************** X*** 14,19 **** X--- 14,135 ---- X extern struct passwd *getpwuid(); X #endif X X+ #if ANY_UNIX /* { */ X+ /* Since elvprsv runs as SUID-root, we need a *secure* version of popen() */ X+ #define popen safe_popen X+ #define pclose safe_pclose X+ X+ /* This function is similar to the standard popen() function, except for... X+ * 1) It doesn't use the shell, for security reasons. X+ * 2) Shell services are not supported, including quoting. X+ * 3) The mode can only be "w". "r" is not supported. X+ * 4) No more than 9 arguments can be given, including the command. X+ */ X+ /*ARGSUSED*/ X+ static FILE *safe_popen(cmd, mode) X+ char *cmd; /* the filename of the program to be run */ X+ char *mode; /* "w", ignored */ X+ { X+ char path[100];/* full pathname of argv[0] */ X+ char *argv[10];/* the arguments */ X+ int r0w1[2];/* the pipe fd's */ X+ int i; X+ FILE *fp; X+ X+ /* parse the arguments */ X+ for (i = 0; i < 9 && *cmd; i++) X+ { X+ /* remember where this arg starts */ X+ argv[i] = cmd; X+ X+ /* move to the end of the argument */ X+ do X+ { X+ cmd++; X+ } while (*cmd && *cmd != ' '); X+ X+ /* then mark end of arg & skip to next */ X+ while (*cmd && *cmd == ' ') X+ { X+ *cmd++ = '\0'; X+ } X+ printf("argv[%d]=\"%s\"\n", i, argv[i]); X+ } X+ argv[i] = (char *)0; X+ X+ /* make the pipe */ X+ if (pipe(r0w1) < 0) X+ { X+ perror("pipe()"); X+ return (FILE *)0; /* pipe failed */ X+ } X+ X+ switch (fork()) X+ { X+ case -1: /* error */ X+ perror("fork()"); X+ return (FILE *)0; X+ X+ case 0: /* child */ X+ /* close the "write" end of the pipe */ X+ close(r0w1[1]); X+ X+ /* redirect stdin to come from the "read" end of the pipe */ X+ close(0); X+ dup(r0w1[0]); X+ close(r0w1[0]); X+ X+ /* exec the shell to run the command */ X+ if (*argv[0] != '/') X+ { X+ /* no path, try "/bin/argv[0]" */ X+ strcpy(path, "/bin/"); X+ strcat(path, argv[0]); X+ execv(path, argv); X+ perror(path); X+ X+ /* if that failed, then try "/usr/bin/argv[0]" */ X+ strcpy(path, "/usr/bin/"); X+ strcat(path, argv[0]); X+ execv(path, argv); X+ perror(path); X+ } X+ else X+ { X+ /* full pathname given, so use it */ X+ execv(argv[0], argv); X+ perror(argv[0]); X+ } X+ X+ /* if we get here, exec failed */ X+ exit(1); X+ X+ default: /* parent */ X+ /* close the "read" end of the pipe */ X+ close(r0w1[0]); X+ X+ /* convert the "write" fd into a (FILE *) */ X+ fp = fdopen(r0w1[1], "w"); X+ return fp; X+ } X+ /*NOTREACHED*/ X+ } X+ X+ X+ /* This function closes the pipe opened by popen(), and returns 0 for success */ X+ static int safe_pclose(fp) X+ FILE *fp; /* value returned by popen() */ X+ { X+ int status; X+ X+ /* close the file, and return the defunct child's exit status */ X+ fclose(fp); X+ wait(&status); X+ return status; X+ } X+ #endif /* } ANY UNIX */ X+ X+ X /* This variable is used to add extra error messages for mail sent to root */ X char *ps; X X*************** X*** 53,59 **** X char *when; /* description of why the file was preserved */ X { X char cmd[80];/* buffer used for constructing a "mail" command */ X! FILE *m, *popen(); /* stream used for giving text to the "mail" program */ X char *base; /* basename of the file */ X X /* separate the directory name from the basename. */ X--- 169,175 ---- X char *when; /* description of why the file was preserved */ X { X char cmd[80];/* buffer used for constructing a "mail" command */ X! FILE *m; /* stream used for giving text to the "mail" program */ X char *base; /* basename of the file */ X X /* separate the directory name from the basename. */ X*************** X*** 75,85 **** X #if OSK X sprintf(cmd, "mail \"-s=%s preserved!\" %s", base, user); X #else /* ANY_UNIX */ X! sprintf(cmd, "mail %s >/dev/null 2>/dev/null", user); X #endif X m = popen(cmd, "w"); X if (!m) X { X /* Can't send mail! Hope the user figures it out. */ X return; X } X--- 191,202 ---- X #if OSK X sprintf(cmd, "mail \"-s=%s preserved!\" %s", base, user); X #else /* ANY_UNIX */ X! sprintf(cmd, "mail -s Graceland %s", user); X #endif X m = popen(cmd, "w"); X if (!m) X { X+ perror(cmd); X /* Can't send mail! Hope the user figures it out. */ X return; X } X*** ../1.7/config.h Fri May 14 11:39:14 1993 X--- config.h Fri May 21 10:59:47 1993 X*************** X*** 476,481 **** X--- 476,487 ---- X # ifndef TMPDIR X # define TMPDIR "sys$scratch:" /* directory where temp files live */ X # endif X+ # ifndef PRSVDIR X+ # define PRSVDIR "sys$scratch:" /* directory where preserved file live */ X+ # endif X+ # ifndef PRSVINDEX X+ # define PRSVINDEX "sys$scratch:index;1" /* index of files in PRSVDIR */ X+ # endif X # define TMPNAME "%selv_%x.%x;1" /* temp file */ X # define SCRATCHIN "%ssiXXXXXX" /* DOS ONLY - output of filter program */ X # define SCRATCHOUT "%ssoXXXXXX" /* temp file used as input to filter */ eof if test `wc -c