NetBSD Problem Report #3019

Received: (qmail-queue invoked from smtpd); 11 Dec 1996 10:30:18 -0000
Message-Id: <199612111027.LAA01377@antifer.ibp.fr>
Date: Wed, 11 Dec 1996 11:27:01 +0100 (MET)
From: Manuel BOUYER <bouyer@antioche.ibp.fr>
Reply-To: bouyer@antioche.ibp.fr
To: gnats-bugs@gnats.netbsd.org
Subject: a client can write an read-only exported file-system
X-Send-Pr-Version: 3.95

>Number:         3019
>Category:       kern
>Synopsis:       a client can write an read-only exported file-system
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          feedback
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Dec 11 02:35:00 +0000 1996
>Closed-Date:    
>Last-Modified:  Wed Apr 03 03:38:25 +0000 2024
>Originator:     Manuel BOUYER
>Release:        1.2_BETA
>Organization:

MASI, Universite Paris VI.

>Environment:

NetBSD antioche.ibp.fr 1.2_BETA NetBSD 1.2_BETA (ANTIOCHE) #0: Tue Oct 22 14:36:40 MET DST 1996     bouyer@dess106.ibp.fr:/usr/src/src_current/sys/arch/i386/compile/ANTIOCHE i386

>Description:
	An read-only exported NFS file system can be written by (at last)
	an NetBSD 1.2 client. I guess this is a bug on the server side, so any
	clients can write it (but I only have root access to NetBSD hosts, so
	I can't verify it)
>How-To-Repeat:

	antioche#/>cat /etc/exports 
	/home/ftp -ro -network 132.227.61.0 -mask 255.255.255.0
	antioche#/>ps ax|grep mount
	   59 ??  IWs    0:01.50 mountd 
	   23910 p0  S+     0:00.18 grep mount 
	antioche#/>kill -HUP 59

	[note the -ro flag for /home/ftp]

	antifer#/promethee/bouyer>mount antioche:/home/ftp /mnt
	antifer#/promethee/bouyer>mount antioche:/home/ftp /mnt
	antifer#/promethee/bouyer>su - bouyer
	antifer:/promethee/bouyer>cd /mnt/pub/NetBSD
	antifer:/mnt/pub/NetBSD>ls -ld .
	drwxr-xr-x  5 bouyer  wheel  512 Oct 29 15:10 .
	antifer:/mnt/pub/NetBSD>
	antifer:/mnt/pub/NetBSD>ls
	NetBSD-current  sup             unofficial
	antifer:/mnt/pub/NetBSD>cat >toto
	qwerty
	antifer:/mnt/pub/NetBSD>ls
	NetBSD-current  sup             toto            unofficial

	[this file also appears on the server:
	antioche#/>ls /home/ftp/pub/NetBSD/
	.message        NetBSD-current  sup             toto            unofficial
	antioche#/>cat /home/ftp/pub/NetBSD/toto 
	qwerty

>Fix:
	Unknown.
>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->analyzed 
State-Changed-By: fvdl 
State-Changed-When: Wed Dec 11 03:55:31 PST 1996 
State-Changed-Why:  

From the exports manual page, BUGS section:

     The export options are tied to the local mount points in the kernel and
     must be non-contradictory for any exported subdirectory of the local
     server mount point.

This is what was happening here. It's still a bug, but less serious, since
this behaviour doesn't occur for every single mount.

From: Taylor R Campbell <riastradh@NetBSD.org>
To: Manuel Bouyer <manuel.bouyer@lip6.fr>
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Subject: Re: kern/3019: a client can write to a read-only exported file system
Date: Tue, 2 Apr 2024 22:13:22 +0000

 Can you still reproduce PR 3019?

 I can't reproduce it.

 I set up an nfs server with a read/write file system /export/erlite3,
 and a read-only export in /etc/exports, on 10.10.2.1:

 server# grep -e -ro /etc/exports
 /export/erlite3 -ro -network 10.10.2.0/24 -maproot=3Droot:wheel
 server# mount | grep erlite3
 rpool/export/erlite3 on /export/erlite3 type zfs (noatime, NFS exported, lo=
 cal)

 I mounted the file system read/write on the client 10.10.2.2:

 client# mount
 10.10.2.1:/export/erlite3 on / type nfs

 And write attempts on the client fail with EROFS:

 client# cd /tmp
 client# touch x
 touch: x: Read-only file system
 client# cat >toto
 -sh: cannot create toto: read-only file system


 I reviewed all the NFS server RPC definitions in sys/nfs, and I
 couldn't find one that fails to block writes on read-only exports with
 EROFS.

 Here's my notes from auditing these paths.  I also skimmed through the
 same paths in the netbsd-1-1 branch, and I couldn't find any holes
 there either, although I don't have any 1.1 or 1.2_BETA systems handy
 to test.  So I'm struggling to find how this bug could have ever been
 there, and if it was, how it was fixed in the time between when it was
 filed and now.


 * nfsrv_null

 doesn't do anything

 * nfsrv_getattr

 read-only, VOP_GETATTR

 * nfsrv_setattr

 All paths go through a read-only check that fails with EROFS:

     359 	if (va.va_size =3D=3D ((u_quad_t)((quad_t) -1))) {
     360 		if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) {
     361 			error =3D EROFS;
     362 			goto out;
     363 		}
     364 	} else {
     365 		if (vp->v_type =3D=3D VDIR) {
     366 			error =3D EISDIR;
     367 			goto out;
     368 		} else if ((error =3D nfsrv_access(vp, VWRITE, cred, rdonly,
     369 			lwp, 0)) !=3D 0)
     370 			goto out;
     371 	}
     372 	error =3D VOP_SETATTR(vp, &va, cred);

 https://nxr.netbsd.org/xref/src/sys/nfs/nfs_serv.c?r=3D1.184#359

 * nfsrv_lookup

 read-only, namei LOOKUP

 * nfsrv3_access

 read-only, VOP_GETATTR and VOP_ACCESS

 * nfsrv_readlink

 read-only, VOP_READLINK and VOP_GETATTR

 * nfsrv_read

 read-only, VOP_GETATTR, VOP_ACCESS, VOP_READ, uvm loan

 * nfsrv_write

 All paths to VOP_WRITE go through nfsrv_access check:

     927 	error =3D nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
     928 		 &rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
     929 	if (error) {
     930 		nfsm_reply(2 * NFSX_UNSIGNED);
     931 		nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
     932 		return (0);
     933 	}
     934 	if (v3)
     935 		forat_ret =3D VOP_GETATTR(vp, &forat, cred);
     936 	if (vp->v_type !=3D VREG) {
     937 		if (v3)
     938 			error =3D EINVAL;
     939 		else
     940 			error =3D (vp->v_type =3D=3D VDIR) ? EISDIR : EACCES;
     941 	}
     942 	if (!error) {
     943 		nqsrv_getl(vp, ND_WRITE);
     944 		error =3D nfsrv_access(vp, VWRITE, cred, rdonly, lwp, 1);
     945 	}
     946 	if (error) {
     947 		vput(vp);
     948 		nfsm_reply(NFSX_WCCDATA(v3));
     949 		nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
     950 		return (0);
     951 	}
 ...
     984 		error =3D VOP_WRITE(vp, uiop, ioflags, cred);

 https://nxr.netbsd.org/xref/src/sys/nfs/nfs_serv.c?r=3D1.184#927

 * nfsrv_create

 This goes through namei with nameiop CREATE:


    1450 	error =3D nfs_namei(&nd, &nsfh, len, slp, nam, &md, &dpos,
    1451 		&dirp, (v3 ? &dirfor_ret : NULL), &dirfor,
    1452 		lwp, (nfsd->nd_flag & ND_KERBAUTH), false);

 https://nxr.netbsd.org/xref/src/sys/nfs/nfs_serv.c?r=3D1.184#1450

     177 	error =3D nfsrv_fhtovp(nsfh, false, &dp, ndp->ni_cnd.cn_cred, slp,
     178 	    nam, &rdonly, kerbflag, pubflag);
     179 	if (error)
     180 		goto out;
     181 	if (dp->v_type !=3D VDIR) {
     182 		vrele(dp);
     183 		error =3D ENOTDIR;
     184 		goto out;
     185 	}
     186=20
     187 	if (rdonly)
     188 		cnp->cn_flags |=3D RDONLY;
 ...
     271 	error =3D lookup_for_nfsd(ndp, dp, neverfollow);

 https://nxr.netbsd.org/xref/src/sys/nfs/nfs_srvsubs.c#177

 lookup_for_nfsd goes through namei_tryemulroot:

    1978 	namei_init(&state, ndp);
    1979 	error =3D namei_tryemulroot(&state,
    1980 				  neverfollow, 1/*inhibitmagic*/, 1/*isnfsd*/);
    1981 	namei_cleanup(&state);
    1982=20
    1983 	if (error) {
    1984 		/* make sure no stray refs leak out */
    1985 		KASSERT(ndp->ni_dvp =3D=3D NULL);
    1986 		KASSERT(ndp->ni_vp =3D=3D NULL);
    1987 	}
    1988=20
    1989 	return error;

 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1978

 namei_tryemulroot goes through namei_oneroot:

    1906 	error =3D namei_oneroot(state, neverfollow, inhibitmagic, isnfsd);

 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1906

 namei_oneroot sets state->rdonly according to cnp->cn_flags & RDONLY:

    1507 	state->rdonly =3D cnp->cn_flags & RDONLY;

 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1507

 It starts with lookup_fastforward:

    1533 		/*
    1534 		 * Parse out the first path name component that we need to
    1535 		 * to consider.  While doing this, attempt to use the name
    1536 		 * cache to fast-forward through as many "easy" to find
    1537 		 * components of the path as possible.
    1538 		 */
    1539 		error =3D lookup_fastforward(state, &searchdir, &foundobj);

 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1533

 But that deliberately doesn't handle the last component:

    1306 		/*
    1307 		 * Can't deal with last component when modifying; this needs
    1308 		 * searchdir locked and VOP_LOOKUP() called (which can and
    1309 		 * does modify state, despite the name).  NB: this case means
    1310 		 * terminal is never set true when LOCKPARENT.
    1311 		 */
    1312 		if ((cnp->cn_flags & ISLASTCN) !=3D 0) {
    1313 			if (cnp->cn_nameiop !=3D LOOKUP ||
    1314 			    (cnp->cn_flags & LOCKPARENT) !=3D 0) {
    1315 				error =3D EOPNOTSUPP;
    1316 				break;
    1317 			}
    1318 		}

 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1306

 Back in namei_oneroot, it calls into lookup_once to handle the last
 step:

    1541 		/*
    1542 		 * If we didn't get a good answer from the namecache, then
    1543 		 * go directly to the file system.
    1544 		 */
    1545 		if (error =3D=3D EOPNOTSUPP) {
    1546 			error =3D lookup_once(state, searchdir, &searchdir,
    1547 			    &foundobj, &searchdir_locked);
    1548 		}

 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1541

 And for the end of a path, lookup_once refuses with EROFS to create
 anything if state->rdonly is set:

    1201 		/*
    1202 		 * If creating and at end of pathname, then can consider
    1203 		 * allowing file to be created.
    1204 		 */
    1205 		if (state->rdonly) {
    1206 			error =3D EROFS;
    1207 			goto done;
    1208 		}

 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1201

 * nfsrv_mkdir

 Blocked by namei CREATE like nfsrv_create.

 * nfsrv_symlink

 Blocked by namei CREATE like nfsrv_create.

 * nfsrv_mknod

 Blocked by namei CREATE like nfsrv_create.

 * nfsrv_remove

 Blocked by namei DELETE:

    1818 		/*
    1819 		 * Disallow directory write attempts on read-only lookups.
    1820 		 * Prefers EEXIST over EROFS for the CREATE case.
    1821 		 */
    1822 		if (state->rdonly &&
    1823 		    (cnp->cn_nameiop =3D=3D DELETE || cnp->cn_nameiop =3D=3D RENA=
 ME)) {
    1824 			if (searchdir) {
    1825 				if (searchdir_locked) {
    1826 					vput(searchdir);
    1827 					searchdir_locked =3D false;
    1828 				} else {
    1829 					vrele(searchdir);
    1830 				}
    1831 				searchdir =3D NULL;
    1832 			}
    1833 			vrele(foundobj);
    1834 			foundobj =3D NULL;
    1835 			ndp->ni_dvp =3D NULL;
    1836 			ndp->ni_vp =3D NULL;
    1837 			state->attempt_retry =3D 1;
    1838 			return EROFS;
    1839 		}

 https://nxr.netbsd.org/xref/src/sys/kern/vfs_lookup.c?r=3D1.234#1818

 * nfsrv_rmdir

 Blocked by namei DELETE like nfsrv_remove.

 * nfsrv_rename

 Blocked by namei DELETE and RENAME like nfsrv_remove.

 * nfsrv_link

 Blocked by namei CREATE like nfsrv_create.

 * nfsrv_readdir

 read-only, VOP_GETATTR and VOP_READDIR

 * nfsrv_readdirplus

 read-only, VOP_GETATTR, VOP_READDIR, and VOP_VGET

 * nfsrv_statfs

 read-only, VFS_STATVFS and VOP_GETATTR

 * nfsrv_fsinfo

 read-only, VOP_GETATTR

 * nfsrv_pathconf

 read-only, VOP_PATHCONF

 * nfsrv_commit

 XXX VOP_FSYNC -- maybe this should be forbidden?

 * nfsrv_noop

 read-only, noop

State-Changed-From-To: analyzed->feedback
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Wed, 03 Apr 2024 03:38:25 +0000
State-Changed-Why:
feedback requested


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2024 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.