PATCH against 2.1.72: better symlinks for a better future (was Re: security warning)

Kevin Buhr (buhr@stat.wisc.edu)
16 Dec 1997 18:15:04 -0600


Okay. Here's the story. The enclosed patch against 2.1.72 does two
things.

First, it handles O_CREAT|O_EXCL as a special case, EEXISTing on
symlinks, dangling or not. This is the same way Solaris 2.5.1 does
it, as documented in its open(2) manpage. Despite the inconsistency,
this seems the most sensible/compatible solution.

Second, it doesn't follow symlinks on hardlink newnames. It *does*
follow them on hardlink oldnames. This is the same as Digital Unix 4.0.

By the way, Solaris 2.5.1 is similar; it never follows destination
symlinks. However, if the source is a dangling symlink, it will
actually let you create a hardlink to the dangling symlink (at least
in a "ufs" filesystem), producing two symlinks with the same inode!
If the source is a non-dangling symlink, it follows it, producing a
link to the underlying file. Ugh x 2!

Kevin <buhr@stat.wisc.edu>

* * *

Index: namei.c
===================================================================
RCS file: /usr/local/src/cvsroot/linux/fs/namei.c,v
retrieving revision 1.1.1.10
diff -u -r1.1.1.10 linux/fs/namei.c
--- linux/fs/namei.c 1997/12/16 22:59:44 1.1.1.10
+++ linux/fs/namei.c 1997/12/17 01:30:22
@@ -73,6 +73,10 @@
* and in the old Linux semantics.
*/

+/* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink
+ * semantics. See the comments in "open_namei" and "do_link" below.
+ */
+
static char * quicklist = NULL;
static int quickcount = 0;
struct semaphore quicklock = MUTEX;
@@ -530,7 +534,13 @@
mode &= S_IALLUGO & ~current->fs->umask;
mode |= S_IFREG;

- dentry = lookup_dentry(pathname, NULL, 1);
+ /*
+ * Special case: O_CREAT|O_EXCL on a dangling symlink should
+ * give EEXIST for security reasons. While inconsistent, this
+ * is the same scheme used by, for example, Solaris 2.5.1. --KAB
+ */
+ dentry = lookup_dentry(pathname, NULL,
+ (flag & (O_CREAT|O_EXCL)) != (O_CREAT|O_EXCL));
if (IS_ERR(dentry))
return dentry;

@@ -1010,7 +1020,16 @@
if (IS_ERR(old_dentry))
goto exit;

- new_dentry = lookup_dentry(newname, NULL, 1);
+ /*
+ * Hardlinks are often used in delicate situations. We avoid
+ * security-related surprises by not following symlinks on the
+ * newname. We *do* follow them on the oldname. This is
+ * the same as Digital Unix 4.0, for example.
+ *
+ * Solaris 2.5.1 is similar, but for a laugh try linking from
+ * a dangling symlink. --KAB
+ */
+ new_dentry = lookup_dentry(newname, NULL, 0);
error = PTR_ERR(new_dentry);
if (IS_ERR(new_dentry))
goto exit_old;