Module Name: src Committed By: snj Date: Sun Nov 5 20:04:00 UTC 2017
Modified Files: src/dist/nvi/common [netbsd-6-1]: recover.c src/usr.bin/nvi/recover [netbsd-6-1]: virecover Log Message: Pull up following revision(s) (requested by spz in ticket #1509): external/bsd/nvi/usr.bin/recover/virecover: 1.2-1.3 via patch external/bsd/nvi/dist/common/recover.c: revision 1.6-1.9 via patch be more careful about opening recovery files... in particular deal with people trying to get 'vi -r' stuck using named pipes, symlink attacks, and coercing others opening recovery files they did not create. Put back the tests for "no files matched" (in a different way than they were written previously - but that's just style.) This is not csh... Use the correct test operator to test for an empty file (rather than testing for an empty file name...) Write test ('[') commands in a way that is defined to work, rather than just happens to - we can afford the (negligible) performance hit here. - don't use command substitution to glob a pattern into a list of filenames; it is less efficient than doing it directly and does not handle whitespace in filenames properly. - change test to [ - quote variables Deal safely with recovery mail files. oops, accidendally committed an earlier non-working version; fixed. Don't use popenve() for portability; forking an extra shell here is not an issue. To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.3.24.1 src/dist/nvi/common/recover.c cvs rdiff -u -r1.1 -r1.1.36.1 src/usr.bin/nvi/recover/virecover Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/dist/nvi/common/recover.c diff -u src/dist/nvi/common/recover.c:1.3 src/dist/nvi/common/recover.c:1.3.24.1 --- src/dist/nvi/common/recover.c:1.3 Sun Jan 18 03:45:50 2009 +++ src/dist/nvi/common/recover.c Sun Nov 5 20:04:00 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: recover.c,v 1.3 2009/01/18 03:45:50 lukem Exp $ */ +/* $NetBSD: recover.c,v 1.3.24.1 2017/11/05 20:04:00 snj Exp $ */ /*- * Copyright (c) 1993, 1994 @@ -112,7 +112,7 @@ static const char sccsid[] = "Id: recove #define VI_PHEADER "X-vi-recover-path: " static int rcv_copy __P((SCR *, int, char *)); -static void rcv_email __P((SCR *, char *)); +static void rcv_email __P((SCR *, const char *)); static char *rcv_gets __P((char *, size_t, int)); static int rcv_mailfile __P((SCR *, int, char *)); static int rcv_mktemp __P((SCR *, char *, const char *, int)); @@ -470,6 +470,23 @@ err: if (!issync) } /* + * Since vi creates recovery files only accessible by the user, files + * accessible by group or others are probably malicious so avoid them. + * This is simpler than checking for getuid() == st.st_uid and we want + * to preserve the functionality that root can recover anything which + * means that root should know better and be careful. + */ +static int +checkok(int fd) +{ + struct stat sb; + + return fstat(fd, &sb) != -1 && S_ISREG(sb.st_mode) && + (sb.st_mode & (S_IRWXG|S_IRWXO)) == 0; +} + + +/* * people making love * never exactly the same * just like a snowflake @@ -513,9 +530,14 @@ rcv_list(SCR *sp) * if we're using fcntl(2), there's no way to lock a file * descriptor that's not open for writing. */ - if ((fp = fopen(dp->d_name, "r+")) == NULL) + if ((fp = fopen(dp->d_name, "r+efl")) == NULL) continue; + if (!checkok(fileno(fp))) { + (void)fclose(fp); + continue; + } + switch (file_lock(sp, NULL, NULL, fileno(fp), 1)) { case LOCK_FAILED: /* @@ -626,9 +648,16 @@ rcv_read(SCR *sp, FREF *frp) * if we're using fcntl(2), there's no way to lock a file * descriptor that's not open for writing. */ - if ((fd = open(recpath, O_RDWR, 0)) == -1) + if ((fd = open(recpath, O_RDWR|O_NONBLOCK|O_NOFOLLOW|O_CLOEXEC, + 0)) == -1) continue; + if (!checkok(fd)) { + (void)close(fd); + continue; + } + + switch (file_lock(sp, NULL, NULL, fd, 1)) { case LOCK_FAILED: /* @@ -836,24 +865,48 @@ rcv_mktemp(SCR *sp, char *path, const ch * Send email. */ static void -rcv_email(SCR *sp, char *fname) +rcv_email(SCR *sp, const char *fname) { struct stat sb; - char buf[MAXPATHLEN * 2 + 20]; + char buf[BUFSIZ]; + FILE *fin, *fout; + size_t l; - if (_PATH_SENDMAIL[0] != '/' || stat(_PATH_SENDMAIL, &sb)) + if (_PATH_SENDMAIL[0] != '/' || stat(_PATH_SENDMAIL, &sb) == -1) { msgq_str(sp, M_SYSERR, _PATH_SENDMAIL, "071|not sending email: %s"); - else { - /* - * !!! - * If you need to port this to a system that doesn't have - * sendmail, the -t flag causes sendmail to read the message - * for the recipients instead of specifying them some other - * way. - */ - (void)snprintf(buf, sizeof(buf), - "%s -t < %s", _PATH_SENDMAIL, fname); - (void)system(buf); + return; } + + /* + * !!! + * If you need to port this to a system that doesn't have + * sendmail, the -t flag causes sendmail to read the message + * for the recipients instead of specifying them some other + * way. + */ + if ((fin = fopen(fname, "refl")) == NULL) { + msgq_str(sp, M_SYSERR, + fname, "325|cannot open: %s"); + return; + } + + if (!checkok(fileno(fin))) { + (void)fclose(fin); + return; + } + + fout = popen(_PATH_SENDMAIL " -t", "w"); + if (fout == NULL) { + msgq_str(sp, M_SYSERR, + _PATH_SENDMAIL, "326|cannot execute sendmail: %s"); + fclose(fin); + return; + } + + while ((l = fread(buf, 1, sizeof(buf), fin)) != 0) + (void)fwrite(buf, 1, l, fout); + + (void)fclose(fin); + (void)pclose(fout); } Index: src/usr.bin/nvi/recover/virecover diff -u src/usr.bin/nvi/recover/virecover:1.1 src/usr.bin/nvi/recover/virecover:1.1.36.1 --- src/usr.bin/nvi/recover/virecover:1.1 Tue Sep 2 09:25:39 2008 +++ src/usr.bin/nvi/recover/virecover Sun Nov 5 20:04:00 2017 @@ -1,6 +1,6 @@ #!/bin/sh - # -# $NetBSD: virecover,v 1.1 2008/09/02 09:25:39 christos Exp $ +# $NetBSD: virecover,v 1.1.36.1 2017/11/05 20:04:00 snj Exp $ # # @(#)recover.in 8.8 (Berkeley) 10/10/96 # @@ -10,40 +10,44 @@ RECDIR="/var/tmp/vi.recover" SENDMAIL="/usr/sbin/sendmail" # Check editor backup files. -vibackup=`echo $RECDIR/vi.*` -if [ "$vibackup" != "$RECDIR/vi.*" ]; then - for i in $vibackup; do - # Only test files that are readable. - if test ! -f $i || test ! -r $i; then - continue - fi - - # Unmodified nvi editor backup files either have the - # execute bit set or are zero length. Delete them. - if test -x $i -o ! -s $i; then - rm $i - fi - done -fi +for i in $RECDIR/vi.*; do + + case "$i" in + $RECDIR/vi.\*) continue;; + esac + + # Only test files that are readable. + if ! [ -f "$i" ] || ! [ -r "$i" ]; then + continue + fi + + # Unmodified nvi editor backup files either have the + # execute bit set or are zero length. Delete them. + if [ -x "$i" ] || ! [ -s "$i" ]; then + rm -f "$i" + fi +done # It is possible to get incomplete recovery files, if the editor crashes # at the right time. -virecovery=`echo $RECDIR/recover.*` -if [ "$virecovery" != "$RECDIR/recover.*" ]; then - for i in $virecovery; do - # Only test files that are readable. - if test ! -r $i; then - continue - fi - - # Delete any recovery files that are zero length, corrupted, - # or that have no corresponding backup file. Else send mail - # to the user. - recfile=`awk '/^X-vi-recover-path:/{print $2}' < $i` - if test -n "$recfile" -a -s "$recfile"; then - $SENDMAIL -t < $i - else - rm $i - fi - done -fi +for i in $RECDIR/recover.*; do + + case "$i" in + $RECDIR/recover.\*) continue;; + esac + + # Only test files that are readable. + if ! [ -r "$i" ]; then + continue + fi + + # Delete any recovery files that are zero length, corrupted, + # or that have no corresponding backup file. Else send mail + # to the user. + recfile=$(awk '/^X-vi-recover-path:/{print $2}' < "$i") + if [ -n "$recfile" ] && [ -s "$recfile" ]; then + $SENDMAIL -t < "$i" + else + rm -f "$i" + fi +done