Index: lib/libc/sys/read.2 =================================================================== --- lib/libc/sys/read.2 +++ lib/libc/sys/read.2 @@ -28,7 +28,7 @@ .\" @(#)read.2 8.4 (Berkeley) 2/26/94 .\" $FreeBSD$ .\" -.Dd March 30, 2020 +.Dd May 15, 2020 .Dt READ 2 .Os .Sh NAME @@ -199,9 +199,14 @@ The file was marked for non-blocking I/O, and no data were ready to be read. .It Bq Er EISDIR -The file descriptor is associated with a directory residing -on a file system that does not allow regular read operations on -directories (e.g.\& NFS). +The file descriptor is associated with a directory. +Directories may only be read directly by root if the filesystem supports it and +the +.Dv security.bsd.allow_read_dir +sysctl MIB is set to a non-zero value. +For most scenarios, the +.Xr readdir 3 +function should be used instead. .It Bq Er EOPNOTSUPP The file descriptor is associated with a file system and file type that do not allow regular read operations on it. Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c @@ -646,6 +646,12 @@ ZFS_ENTER(zfsvfs); ZFS_VERIFY_ZP(zp); + /* We don't copy out anything useful for directories. */ + if (vp->v_type == VDIR) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EISDIR)); + } + if (zp->z_pflags & ZFS_AV_QUARANTINED) { ZFS_EXIT(zfsvfs); return (SET_ERROR(EACCES)); Index: sys/kern/vfs_vnops.c =================================================================== --- sys/kern/vfs_vnops.c +++ sys/kern/vfs_vnops.c @@ -135,6 +135,11 @@ SYSCTL_ULONG(_debug, OID_AUTO, vn_io_faults, CTLFLAG_RD, &vn_io_faults_cnt, 0, "Count of vn_io_fault lock avoidance triggers"); +static int vfs_allow_read_dir = 0; +SYSCTL_INT(_security_bsd, OID_AUTO, allow_read_dir, CTLFLAG_RW, + &vfs_allow_read_dir, 0, + "Enable read(2) of directory by root for filesystems that support it"); + /* * Returns true if vn_io_fault mode of handling the i/o request should * be used. @@ -1160,6 +1165,24 @@ doio = uio->uio_rw == UIO_READ ? vn_read : vn_write; vp = fp->f_vnode; + + /* + * The ability to read(2) on a directory has historically been + * allowed for all users, but this can and has been the source of + * at least one security issue in the past. As such, it is now hidden + * away behind a sysctl for those that actually need it to use it, and + * restricted to root when it's turned on to make it relatively safe to + * leave on for longer sessions of need. + */ + if (vp->v_type == VDIR) { + KASSERT(uio->uio_rw == UIO_READ, + ("illegal write attempted on a directory")); + if (!vfs_allow_read_dir) + return (EISDIR); + if ((error = priv_check(td, PRIV_VFS_READ_DIR)) != 0) + return (EISDIR); + } + foffset_lock_uio(fp, uio, flags); if (do_vn_io_fault(vp, uio)) { args.kind = VN_IO_FAULT_FOP; Index: sys/sys/priv.h =================================================================== --- sys/sys/priv.h +++ sys/sys/priv.h @@ -283,6 +283,7 @@ #define PRIV_VFS_SYSFLAGS 342 /* Can modify system flags. */ #define PRIV_VFS_UNMOUNT 343 /* Can unmount(). */ #define PRIV_VFS_STAT 344 /* Override vnode MAC stat perm. */ +#define PRIV_VFS_READ_DIR 345 /* Can read(2) a dirfd, needs sysctl. */ /* * Virtual memory privileges.