Subject: security/8860: Scripts in /etc attempt to read from files that are under normal user's control.
To: None <gnats-bugs@gnats.netbsd.org>
From: Christos Zoulas <christos@zoulas.com>
List: netbsd-bugs
Date: 11/22/1999 20:15:51
>Number:         8860
>Category:       security
>Synopsis:       Scripts in /etc attempt to read from files that are under normal user's control.
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    security-officer (NetBSD Security Officer)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Nov 22 20:15:00 1999
>Last-Modified:
>Originator:     Christos Zoulas
>Organization:
	I will not take any today, thanks.
>Release:        19991118
>Environment:
System: NetBSD beowulf.gw.com 1.4O NetBSD 1.4O (GW-GENERIC) #2: Thu Nov 18 04:40:59 EST 1999     christos@beowulf.gw.com:/net/hrothgar/src-1/NetBSD/cvsroot/src/sys/arch/i386/compile/GW-GENERIC i386

>Description:
	We don't have an easy method to atomically test for a plain file
	and read. I think fopen() a good place for it.
>How-To-Repeat:
	mknod foo p
	Slow system down, and take advantage of the race
	between:
		if [ -f bar ]; then
			read bar
		fi
	to do:
		mv foo bar
>Fix:
	Patch applied; the fopen and cat change need to be reviewed for
	standards conformance. Other /etc/ scripts need to be audited.
	Also I turned off .rhosts checking because of that, it should
	be turned back on.

src/lib/libc
Index: shlib_version
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/shlib_version,v
retrieving revision 1.78
diff -u -u -r1.78 shlib_version
--- shlib_version	1999/11/15 19:23:20	1.78
+++ shlib_version	1999/11/23 04:00:56
@@ -2,4 +2,4 @@
 #	Remember to update distrib/sets/lists/base/shl.* when changing
 #
 major=12
-minor=50
+minor=51
Index: stdio/flags.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/stdio/flags.c,v
retrieving revision 1.11
diff -u -u -r1.11 flags.c
--- flags.c	1999/09/20 04:39:27	1.11
+++ flags.c	1999/11/23 04:01:04
@@ -91,11 +91,25 @@
 		return (0);
 	}
 
-	/* [rwa]\+ or [rwa]b\+ means read and write */
-	if (*mode == '+' || (*mode == 'b' && mode[1] == '+')) {
-		ret = __SRW;
-		m = O_RDWR;
-	}
+	/*
+	 * [rwa]\+ or [rwa]b\+ means read and write 
+	 * f means open only plain files.
+	 */
+	for (; *mode; mode++)
+		switch (*mode) {
+		case '+':
+			ret = __SRW;
+			m = O_RDWR;
+			break;
+		case 'f':
+			o |= O_NONBLOCK;
+			break;
+		case 'b':
+			break;
+		default:	/* We could produce a warning here */
+			break;
+		}
+
 	*optr = m | o;
 	return (ret);
 }
Index: stdio/fopen.3
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/stdio/fopen.3,v
retrieving revision 1.9
diff -u -u -r1.9 fopen.3
--- fopen.3	1999/01/12 15:27:28	1.9
+++ fopen.3	1999/11/23 04:01:04
@@ -87,12 +87,18 @@
 .Pp
 The
 .Fa mode
-string can also include the letter ``b'' either as a third character or
+string can also include the letter ``b'' either as a last character or
 as a character between the characters in any of the two-character strings
 described above.
 This is strictly for compatibility with
 .St -ansiC
 and has no effect; the ``b'' is ignored.
+In addition the letter ``f'' in the mode string restricts fopen to plain
+files; if the file opened is not a plain file,
+.Fn fopen
+will fail. This is a non
+.St -ansiC
+extension.
 .Pp
 Any created files will have mode
 .Pf \\*q Dv S_IRUSR
@@ -189,6 +195,9 @@
 or
 .Fn freopen
 was invalid.
+.It Bq Er EFTYPE
+The file is not a plain file and the character ``f'' is specified
+in the mode.
 .El
 .Pp
 The
Index: stdio/fopen.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/stdio/fopen.c,v
retrieving revision 1.8
diff -u -u -r1.8 fopen.c
--- fopen.c	1999/09/20 04:39:27	1.8
+++ fopen.c	1999/11/23 04:01:04
@@ -49,6 +49,7 @@
 #include <sys/stat.h>
 #include <assert.h>
 #include <fcntl.h>
+#include <unistd.h>
 #include <stdio.h>
 #include <errno.h>
 #include "local.h"
@@ -67,9 +68,17 @@
 		return (NULL);
 	if ((fp = __sfp()) == NULL)
 		return (NULL);
-	if ((f = open(file, oflags, DEFFILEMODE)) < 0) {
-		fp->_flags = 0;			/* release */
-		return (NULL);
+	if ((f = open(file, oflags, DEFFILEMODE)) < 0)
+		goto release;
+	if (oflags & O_NONBLOCK) {
+		struct stat st;
+		if (fstat(f, &st) == -1)
+			goto release;
+		if (!S_ISREG(st.st_mode)) {
+			errno = EFTYPE;
+			close(f);
+			goto release;
+		}
 	}
 	fp->_file = f;
 	fp->_flags = flags;
@@ -90,4 +99,7 @@
 	if (oflags & O_APPEND)
 		(void) __sseek((void *)fp, (fpos_t)0, SEEK_END);
 	return (fp);
+release:
+	fp->_flags = 0;			/* release */
+	return (NULL);
 }

src/bin/cat
Index: cat.1
===================================================================
RCS file: /cvsroot/basesrc/bin/cat/cat.1,v
retrieving revision 1.16
diff -u -u -r1.16 cat.1
--- cat.1	1999/03/22 18:30:42	1.16
+++ cat.1	1999/11/23 03:59:57
@@ -44,7 +44,7 @@
 .Nd concatenate and print files
 .Sh SYNOPSIS
 .Nm
-.Op Fl benstuv
+.Op Fl befnstuv
 .Op Fl
 .Op Ar
 .Sh DESCRIPTION
@@ -69,6 +69,8 @@
 .Pq Ql \&$
 at the end of each line
 as well.
+.It Fl f
+Only attempt to display plain files.
 .It Fl n
 Number the output lines, starting at 1.
 .It Fl s
Index: cat.c
===================================================================
RCS file: /cvsroot/basesrc/bin/cat/cat.c,v
retrieving revision 1.21
diff -u -u -r1.21 cat.c
--- cat.c	1999/07/08 01:56:09	1.21
+++ cat.c	1999/11/23 03:59:57
@@ -64,7 +64,7 @@
 #include <string.h>
 #include <unistd.h>
 
-int bflag, eflag, nflag, sflag, tflag, vflag;
+int bflag, eflag, fflag, nflag, sflag, tflag, vflag;
 int rval;
 const char *filename;
 
@@ -84,7 +84,7 @@
 
 	(void)setlocale(LC_ALL, "");
 
-	while ((ch = getopt(argc, argv, "benstuv")) != -1)
+	while ((ch = getopt(argc, argv, "befnstuv")) != -1)
 		switch (ch) {
 		case 'b':
 			bflag = nflag = 1;	/* -b implies -n */
@@ -98,6 +98,9 @@
 		case 's':
 			sflag = 1;
 			break;
+		case 'f':
+			fflag = 1;
+			break;
 		case 't':
 			tflag = vflag = 1;	/* -t implies -v */
 			break;
@@ -138,7 +141,7 @@
 		if (*argv) {
 			if (!strcmp(*argv, "-"))
 				fp = stdin;
-			else if ((fp = fopen(*argv, "r")) == NULL) {
+			else if ((fp = fopen(*argv, "rf")) == NULL) {
 				warn("%s", *argv);
 				rval = 1;
 				++argv;
@@ -236,7 +239,24 @@
 		if (*argv) {
 			if (!strcmp(*argv, "-"))
 				fd = fileno(stdin);
+			else if (fflag) {
+				struct stat st;
+				fd = open(*argv, O_RDONLY|O_NONBLOCK, 0);
+				if (fd < 0)
+					goto skip;
+
+				if (fstat(fd, &st) == -1) {
+					close(fd);
+					goto skip;
+				}
+				if (!S_ISREG(st.st_mode)) {
+					close(fd);
+					errno = EFTYPE;
+					goto skip;
+				}
+			}
 			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
+skip:
 				warn("%s", *argv);
 				rval = 1;
 				++argv;

src/etc/security

Index: security
===================================================================
RCS file: /cvsroot/basesrc/etc/security,v
retrieving revision 1.40
diff -u -u -r1.40 security
--- security	1999/09/05 15:11:42	1.40
+++ security	1999/11/23 04:05:46
@@ -328,7 +328,7 @@
 
 	while read uid homedir; do
 		if [ -f ${homedir}/.rhosts -a -r ${homedir}/.rhosts ] && \
-		    egrep '\+' ${homedir}/.rhosts > /dev/null ; then
+		    cat -f ${homedir}/.rhosts | egrep '\+' > /dev/null ; then
 			printf "$uid: + in .rhosts file.\n"
 		fi
 	done < $MPBYPATH > $OUTPUT
>Audit-Trail:
>Unformatted: