From: Ralph Boehme Date: Tue, 29 Sep 2020 08:00:21 +0000 (+0200) Subject: smbd: catch O_PATH opens of symlinks in in non_widelink_open() X-Git-Tag: samba-4.14.0rc1~382 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a272ca549fc7d2e935c3d3a103f3bd3f65b1960e;p=thirdparty%2Fsamba.git smbd: catch O_PATH opens of symlinks in in non_widelink_open() 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 Reviewed-by: Jeremy Allison --- diff --git a/source3/smbd/open.c b/source3/smbd/open.c index b76e5d3e62d..7f0163ffb7b 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -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); /*