]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbd: catch O_PATH opens of symlinks in in non_widelink_open()
authorRalph Boehme <slow@samba.org>
Tue, 29 Sep 2020 08:00:21 +0000 (10:00 +0200)
committerRalph Boehme <slow@samba.org>
Wed, 16 Dec 2020 09:08:30 +0000 (09:08 +0000)
Calling openat() with O_PATH|O_NOFOLLOW will open a handle on the symlink
itself. That would be a nice feature if it would be supported on more platforms,
but being a Linux only thing, we have to preserve the behaviour of failing to
open a handle on symlinks.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/smbd/open.c

index b76e5d3e62d92e01458b0588aed67a1dd3caa772..7f0163ffb7bb84d2245589dc023a57fefade24de 100644 (file)
@@ -642,8 +642,13 @@ static int non_widelink_open(files_struct *fsp,
        struct smb_filename *oldwd_fname = NULL;
        struct smb_filename *parent_dir_fname = NULL;
        struct files_struct *cwdfsp = NULL;
+       bool have_opath = false;
        bool ok;
 
+#ifdef O_PATH
+       have_opath = true;
+#endif
+
        if (fsp->fsp_flags.is_directory) {
                parent_dir_fname = cp_smb_filename(talloc_tos(), smb_fname);
                if (parent_dir_fname == NULL) {
@@ -711,6 +716,45 @@ static int non_widelink_open(files_struct *fsp,
        fsp_set_fd(fsp, fd);
        fsp->fsp_name = tmp_fsp_name;
 
+       if (fd != -1 &&
+           !is_ntfs_stream_smb_fname(fsp->fsp_name) &&
+           fsp->fsp_flags.is_pathref &&
+           have_opath)
+       {
+               /*
+                * Opening with O_PATH and O_NOFOLLOW opens a handle on the
+                * symlink. In follow symlink=yes mode we must avoid this and
+                * instead should open a handle on the symlink target.
+                *
+                * Check for this case by doing an fstat, forcing
+                * process_symlink_open() codepath down below by setting fd=-1
+                * and errno=ELOOP.
+                */
+               int ret;
+
+               fsp->fsp_name = smb_fname_rel;
+
+               ret = SMB_VFS_FSTAT(fsp, &tmp_fsp_name->st);
+
+               fsp->fsp_name = tmp_fsp_name;
+
+               if (ret != 0) {
+                       goto out;
+               }
+
+               if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
+                       fsp->fsp_name = smb_fname_rel;
+
+                       ret = SMB_VFS_CLOSE(fsp);
+                       SMB_ASSERT(ret == 0);
+
+                       fsp_set_fd(fsp, -1);
+                       fsp->fsp_name = tmp_fsp_name;
+                       fd = -1;
+                       errno = ELOOP;
+               }
+       }
+
        if (fd == -1) {
                saved_errno = link_errno_convert(errno);
                /*