Subject: kern/17053: patch to add O_NOFOLLOW to open(2)
To: None <gnats-bugs@gnats.netbsd.org>
From: None <xs@kittenz.org>
List: netbsd-bugs
Date: 05/26/2002 22:16:44
>Number: 17053
>Category: kern
>Synopsis: patch to add O_NOFOLLOW to open(2)
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Sun May 26 14:19:00 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator: xs@kittenz.org
>Release: NetBSD 1.6A (from 26th May)
>Organization:
>Environment:
/sys/kern/vfs_vnops.c:
$NetBSD: vfs_vnops.c,v 1.54 2002/03/17 19:41:08 atatat Exp $
/sys/sys/fcntl.h:
$NetBSD: fcntl.h,v 1.20 2001/12/07 07:09:30 jdolecek Exp $
/sys/kern/kern_sig.c:
$NetBSD: kern_sig.c,v 1.120 2002/03/08 20:48:40 thorpej Exp $
System: NetBSD stasis 1.6A NetBSD 1.6A (STASIS) #2: Sun May 26 21:15:02 BST 2002 xs@stasis:/usr/src/sys/arch/i386/compile/STASIS i386
Architecture: i386
Machine: i386
>Description:
NetBSD's open(2) lacks an O_NOFOLLOW or similar flag when O_CREAT is
not set. Without this flag it is not possible to open a file that
should not be a symlink without a race condition under some
circumstances. (Such as the file residing in an untrusted or world
writable directory.)
>How-To-Repeat:
Currently something like the following must be used:
struct stat sb;
char fname[];
int fd;
lstat(fname, &sb);
if (S_ISLNK(sb.st_mode))
err(1, "symlink!");
/* race occurs here, because file can be replaced with symlink
* after check
*/
fd = open(fname, O_RDONLY, 0);
fstat(fd, &sb);
if (!S_ISREG(sb.st_mode))
...; /* too late. */
Post patch:
struct stat sb;
char fname[];
int fd;
fd = open(fname, O_RDONLY|O_NOFOLLOW, 0);
/* open fails if path is symlink */
fstat(fd, &sb);
if (!S_ISREG(sb.st_mode))
...;
>Fix:
Index: sys/kern/vfs_vnops.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/vfs_vnops.c,v
retrieving revision 1.54
diff -c -r1.54 vfs_vnops.c
*** vfs_vnops.c 2002/03/17 19:41:08 1.54
--- vfs_vnops.c 2002/05/26 21:00:55
***************
*** 91,97 ****
ndp->ni_cnd.cn_nameiop = CREATE;
ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
if ((fmode & O_EXCL) == 0 &&
! ((fmode & FNOSYMLINK) == 0))
ndp->ni_cnd.cn_flags |= FOLLOW;
if ((error = namei(ndp)) != 0)
return (error);
--- 91,97 ----
ndp->ni_cnd.cn_nameiop = CREATE;
ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
if ((fmode & O_EXCL) == 0 &&
! ((fmode & O_NOFOLLOW) == 0))
ndp->ni_cnd.cn_flags |= FOLLOW;
if ((error = namei(ndp)) != 0)
return (error);
***************
*** 120,137 ****
error = EEXIST;
goto bad;
}
- if (ndp->ni_vp->v_type == VLNK) {
- error = EFTYPE;
- goto bad;
- }
fmode &= ~O_CREAT;
}
} else {
ndp->ni_cnd.cn_nameiop = LOOKUP;
! ndp->ni_cnd.cn_flags = FOLLOW | LOCKLEAF;
if ((error = namei(ndp)) != 0)
return (error);
vp = ndp->ni_vp;
}
if (vp->v_type == VSOCK) {
error = EOPNOTSUPP;
--- 120,139 ----
error = EEXIST;
goto bad;
}
fmode &= ~O_CREAT;
}
} else {
ndp->ni_cnd.cn_nameiop = LOOKUP;
! ndp->ni_cnd.cn_flags = LOCKLEAF;
! if ((fmode & O_NOFOLLOW) == 0)
! ndp->ni_cnd.cn_flags |= FOLLOW;
if ((error = namei(ndp)) != 0)
return (error);
vp = ndp->ni_vp;
+ }
+ if (ndp->ni_vp->v_type == VLNK) {
+ error = EFTYPE;
+ goto bad;
}
if (vp->v_type == VSOCK) {
error = EOPNOTSUPP;
Index: sys/kern/kern_sig.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/kern_sig.c,v
retrieving revision 1.120
diff -c -r1.120 kern_sig.c
*** kern_sig.c 2002/03/08 20:48:40 1.120
--- kern_sig.c 2002/05/26 21:00:57
***************
*** 1368,1374 ****
return error;
NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE | FNOSYMLINK, S_IRUSR | S_IWUSR);
if (error)
return (error);
vp = nd.ni_vp;
--- 1368,1374 ----
return error;
NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR);
if (error)
return (error);
vp = nd.ni_vp;
Index: sys/sys/fcntl.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/fcntl.h,v
retrieving revision 1.20
diff -c -r1.20 fcntl.h
*** fcntl.h 2001/12/07 07:09:30 1.20
--- fcntl.h 2002/05/26 21:00:57
***************
*** 95,100 ****
--- 95,102 ----
(_XOPEN_SOURCE - 0) >= 500
#define O_SYNC 0x00000080 /* synchronous writes */
#endif
+ #define O_NOFOLLOW 0x00000100 /* don't follow symlink for
+ last component */
#define O_CREAT 0x00000200 /* create if nonexistent */
#define O_TRUNC 0x00000400 /* truncate to zero length */
#define O_EXCL 0x00000800 /* error if already exists */
***************
*** 119,137 ****
/* all bits settable during open(2) */
#define O_MASK (O_ACCMODE|O_NONBLOCK|O_APPEND|O_SHLOCK|O_EXLOCK|\
! O_ASYNC|O_SYNC|O_CREAT|O_TRUNC|O_EXCL|O_DSYNC|\
! O_RSYNC|O_NOCTTY|O_ALT_IO)
#define FMARK 0x00001000 /* mark during gc() */
#define FDEFER 0x00002000 /* defer for next gc pass */
#define FHASLOCK 0x00004000 /* descriptor holds advisory lock */
- /*
- * Note: The below is not a flag that can be used in the struct file.
- * It's an option that can be passed to vn_open to make sure it doesn't
- * follow a symlink on the last lookup
- */
- #define FNOSYMLINK 0x00080000 /* Don't follow symlink for last
- component */
/* bits to save after open(2) */
#define FMASK (FREAD|FWRITE|FAPPEND|FASYNC|FFSYNC|FNONBLOCK|FDSYNC|\
FRSYNC|FALTIO)
--- 121,132 ----
/* all bits settable during open(2) */
#define O_MASK (O_ACCMODE|O_NONBLOCK|O_APPEND|O_SHLOCK|O_EXLOCK|\
! O_ASYNC|O_SYNC|O_NOFOLLOW|O_CREAT|O_TRUNC|O_EXCL|\
! O_DSYNC|O_RSYNC|O_NOCTTY|O_ALT_IO)
#define FMARK 0x00001000 /* mark during gc() */
#define FDEFER 0x00002000 /* defer for next gc pass */
#define FHASLOCK 0x00004000 /* descriptor holds advisory lock */
/* bits to save after open(2) */
#define FMASK (FREAD|FWRITE|FAPPEND|FASYNC|FFSYNC|FNONBLOCK|FDSYNC|\
FRSYNC|FALTIO)
Index: lib/libc/sys/open.2
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/sys/open.2,v
retrieving revision 1.22
diff -c -r1.22 open.2
*** open.2 2002/02/08 01:28:20 1.22
--- open.2 2002/05/26 21:00:58
***************
*** 103,108 ****
--- 103,114 ----
I/O file integrity completion:
each write will wait for both the file data and file status to be
committed to stable storage.
+ .It Dv O_NOFOLLOW
+ If the last component of
+ .Fa path
+ is a symbolic link,
+ .Fn open
+ will fail.
.It Dv O_RSYNC
If set, read operations will complete at the same level of
integrity which is in effect for write operations:
***************
*** 218,223 ****
--- 224,235 ----
does not permit writing.
.It Bq Er ELOOP
Too many symbolic links were encountered in translating the pathname.
+ .It Bq Er EFTYPE
+ If
+ .Dv O_NOFOLLOW
+ was set, then the last component of
+ .Fa path
+ was a symbolic link.
.It Bq Er EISDIR
The named file is a directory, and the arguments specify
it is to be opened for writing.
>Release-Note:
>Audit-Trail:
>Unformatted: