s3: smbd: fix regression with non-wide symlinks to directories over SMB3.
authorDaniel Kobras <d.kobras@science-computing.de>
Fri, 23 Jun 2017 13:39:21 +0000 (15:39 +0200)
committerJeremy Allison <jra@samba.org>
Mon, 26 Jun 2017 21:16:13 +0000 (23:16 +0200)
The errno returned by open() is ambiguous when called with flags O_NOFOLLOW and
O_DIRECTORY on a symlink. With ELOOP, we know for certain that we've tried to
open a symlink. With ENOTDIR, we might have hit a symlink, and need to perform
further checks to be sure. Adjust non_widelink_open() accordingly. This fixes
a regression where symlinks to directories within the same share were no
longer followed for some call paths on systems returning ENOTDIR in the above
case.

Also remove the knownfail added in previous commit.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12860

Signed-off-by: Daniel Kobras <d.kobras@science-computing.de>
Reviewed-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
selftest/knownfail.d/samba3.blackbox.smbclient_s3.follow_local_symlinks [deleted file]
source3/smbd/open.c

diff --git a/selftest/knownfail.d/samba3.blackbox.smbclient_s3.follow_local_symlinks b/selftest/knownfail.d/samba3.blackbox.smbclient_s3.follow_local_symlinks
deleted file mode 100644 (file)
index 7be44c1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-^samba3.blackbox.smbclient_s3.*follow local symlinks.*
index e68e2ace85083bf2cda5f1f862002f76a4edbb12..fa74f48b66d0abe03a40b5eea66a5dd27ed6a461 100644 (file)
@@ -581,7 +581,18 @@ static int non_widelink_open(struct connection_struct *conn,
 
        if (fd == -1) {
                saved_errno = link_errno_convert(errno);
 
        if (fd == -1) {
                saved_errno = link_errno_convert(errno);
-               if (saved_errno == ELOOP) {
+               /*
+                * Trying to open a symlink to a directory with O_NOFOLLOW and
+                * O_DIRECTORY can return either of ELOOP and ENOTDIR. So
+                * ENOTDIR really means: might be a symlink, but we're not sure.
+                * In this case, we just assume there's a symlink. If we were
+                * wrong, process_symlink_open() will return EINVAL. We check
+                * this below, and fall back to returning the initial
+                * saved_errno.
+                *
+                * BUG: https://bugzilla.samba.org/show_bug.cgi?id=12860
+                */
+               if (saved_errno == ELOOP || saved_errno == ENOTDIR) {
                        if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) {
                                /* Never follow symlinks on posix open. */
                                goto out;
                        if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) {
                                /* Never follow symlinks on posix open. */
                                goto out;
@@ -591,7 +602,7 @@ static int non_widelink_open(struct connection_struct *conn,
                                goto out;
                        }
                        /*
                                goto out;
                        }
                        /*
-                        * We have a symlink. Follow in userspace
+                        * We may have a symlink. Follow in userspace
                         * to ensure it's under the share definition.
                         */
                        fd = process_symlink_open(conn,
                         * to ensure it's under the share definition.
                         */
                        fd = process_symlink_open(conn,
@@ -602,6 +613,15 @@ static int non_widelink_open(struct connection_struct *conn,
                                        mode,
                                        link_depth);
                        if (fd == -1) {
                                        mode,
                                        link_depth);
                        if (fd == -1) {
+                               if (saved_errno == ENOTDIR &&
+                                               errno == EINVAL) {
+                                       /*
+                                        * O_DIRECTORY on neither a directory,
+                                        * nor a symlink. Just return
+                                        * saved_errno from initial open()
+                                        */
+                                       goto out;
+                               }
                                saved_errno =
                                        link_errno_convert(errno);
                        }
                                saved_errno =
                                        link_errno_convert(errno);
                        }