1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

smbd: Extend openat_pathref_dirfsp_nosymlink()

Turn it into openat_pathref_fsp_nosymlink() which opens not only
directories but normal files and symlinks too. If it finds a symlink,
return NT_STATUS_STOPPED_ON_SYMLINK and all the metadata we can find:
struct stat_ex plus the symlink target.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
This commit is contained in:
Volker Lendecke 2022-12-12 14:04:00 +01:00
parent eb2978f55c
commit e9363926dc
3 changed files with 209 additions and 99 deletions

View File

@ -30,6 +30,7 @@
#include "smbd/smbd.h"
#include "smbd/globals.h"
#include "lib/util/memcache.h"
#include "libcli/smb/reparse_symlink.h"
uint32_t ucf_flags_from_smb_request(struct smb_request *req)
{
@ -1095,8 +1096,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
posix ? SMB_FILENAME_POSIX_PATH : 0,
&smb_dirname);
} else {
char *substitute = NULL;
size_t unparsed = 0;
struct open_symlink_err *symlink_err = NULL;
status = normalize_filename_case(conn, dirname, ucf_flags);
if (!NT_STATUS_IS_OK(status)) {
@ -1106,25 +1106,36 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
goto fail;
}
status = openat_pathref_dirfsp_nosymlink(
mem_ctx,
status = openat_pathref_fsp_nosymlink(mem_ctx,
conn,
conn->cwd_fsp,
dirname,
0,
posix,
&smb_dirname,
&unparsed,
&substitute);
&symlink_err);
if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
size_t name_in_len, dirname_len;
size_t name_in_len = strlen(name_in);
size_t dirname_len = strlen(dirname);
if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
strnequal(symlink_err->reparse->substitute_name,
"msdfs:",
6)) {
status = NT_STATUS_PATH_NOT_COVERED;
goto fail;
}
name_in_len = strlen(name_in);
dirname_len = strlen(dirname);
SMB_ASSERT(name_in_len >= dirname_len);
*_substitute = substitute;
*_unparsed = unparsed + (name_in_len - dirname_len);
*_substitute = talloc_move(
mem_ctx,
&symlink_err->reparse->substitute_name);
*_unparsed = symlink_err->unparsed +
(name_in_len - dirname_len);
goto fail;
}
@ -1136,11 +1147,6 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
nt_errstr(status));
TALLOC_FREE(dirname);
if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
/* MS-DFS error must propagate back out. */
goto fail;
}
if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
/*
* Except ACCESS_DENIED, everything else leads
@ -1156,6 +1162,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
goto fail;
}
smb_dirname->fsp->fsp_flags.is_directory = true;
/*
* Only look at bad last component values

View File

@ -808,8 +808,7 @@ openat_pathref_fsp_nosymlink_internal(TALLOC_CTX *mem_ctx,
NTTIME twrp,
bool posix,
struct smb_filename **_smb_fname,
size_t *unparsed,
char **substitute)
struct open_symlink_err **_symlink_err)
{
struct files_struct *dirfsp = in_dirfsp;
struct smb_filename full_fname = {
@ -823,13 +822,14 @@ openat_pathref_fsp_nosymlink_internal(TALLOC_CTX *mem_ctx,
.flags = full_fname.flags,
};
struct smb_filename *result = NULL;
struct open_symlink_err *symlink_err = NULL;
struct files_struct *fsp = NULL;
char *path = NULL, *next = NULL;
bool case_sensitive, ok;
bool case_sensitive, ok, is_toplevel;
int fd;
NTSTATUS status;
struct vfs_open_how how = {
.flags = O_NOFOLLOW|O_DIRECTORY,
.flags = O_NOFOLLOW | O_NONBLOCK,
.mode = 0,
};
@ -868,7 +868,12 @@ openat_pathref_fsp_nosymlink_internal(TALLOC_CTX *mem_ctx,
#endif
#endif
full_fname.base_name = talloc_strdup(talloc_tos(), "");
is_toplevel = (dirfsp == dirfsp->conn->cwd_fsp);
is_toplevel |= ISDOT(dirfsp->fsp_name->base_name);
full_fname.base_name =
talloc_strdup(talloc_tos(),
is_toplevel ? "" : dirfsp->fsp_name->base_name);
if (full_fname.base_name == NULL) {
DBG_DEBUG("talloc_strdup() failed\n");
goto nomem;
@ -929,8 +934,11 @@ openat_pathref_fsp_nosymlink_internal(TALLOC_CTX *mem_ctx,
fd = SMB_VFS_OPENAT(conn, dirfsp, &rel_fname, fsp, &how);
if (fd >= 0) {
fsp_set_fd(fsp, fd);
TALLOC_FREE(full_fname.base_name);
full_fname = rel_fname;
ok = full_path_extend(&full_fname.base_name,
rel_fname.base_name);
if (!ok) {
goto nomem;
}
goto done;
}
@ -1035,54 +1043,104 @@ next:
&how);
}
#ifndef O_PATH
if ((fd == -1) && (errno == ELOOP)) {
int ret;
/*
* O_NOFOLLOW|O_DIRECTORY results in
* ENOTDIR instead of ELOOP.
*
* But we should be prepared to handle ELOOP too.
* openat() hit a symlink. With O_PATH we open the
* symlink and get ENOTDIR in the next round, see
* below.
*/
if ((fd == -1) && (errno == ENOTDIR || errno == ELOOP)) {
NTSTATUS orig_status = map_nt_error_from_unix(errno);
status = readlink_talloc(
mem_ctx, dirfsp, &rel_fname, substitute);
if (NT_STATUS_IS_OK(status)) {
/*
* readlink_talloc() found a symlink
*/
status = NT_STATUS_STOPPED_ON_SYMLINK;
if (unparsed != NULL) {
if (next == NULL) {
*unparsed = 0;
} else {
size_t parsed = next - path;
size_t len = talloc_get_size(path);
*unparsed = len - parsed;
symlink_err = talloc_zero(mem_ctx, struct open_symlink_err);
if (symlink_err == NULL) {
goto nomem;
}
}
/*
* If we're on an MSDFS share, see if this is
* an MSDFS link.
*/
if (lp_host_msdfs() &&
lp_msdfs_root(SNUM(conn)) &&
(substitute != NULL) &&
strnequal(*substitute, "msdfs:", 6) &&
is_msdfs_link(dirfsp, &rel_fname))
{
status = NT_STATUS_PATH_NOT_COVERED;
}
} else {
status = read_symlink_reparse(symlink_err,
dirfsp,
&rel_fname,
&symlink_err->reparse);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("readlink_talloc failed: %s\n",
nt_errstr(status));
/*
* Restore the error status from SMB_VFS_OPENAT()
*/
status = orig_status;
goto fail;
}
if (next != NULL) {
size_t parsed = next - path;
size_t len = talloc_get_size(path);
symlink_err->unparsed = len - parsed;
}
/*
* We know rel_fname is a symlink, now fill in the
* rest of the metadata for our callers.
*/
ret = SMB_VFS_FSTATAT(conn,
dirfsp,
&rel_fname,
&symlink_err->st,
AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
status = map_nt_error_from_unix(errno);
DBG_DEBUG("SMB_VFS_FSTATAT(%s/%s) failed: %s\n",
fsp_str_dbg(dirfsp),
rel_fname.base_name,
strerror(errno));
TALLOC_FREE(symlink_err);
goto fail;
}
if (!S_ISLNK(symlink_err->st.st_ex_mode)) {
/*
* Hit a race: readlink_talloc() worked before
* the fstatat(), but rel_fname changed to
* something that's not a symlink.
*/
status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
TALLOC_FREE(symlink_err);
goto fail;
}
status = NT_STATUS_STOPPED_ON_SYMLINK;
goto fail;
}
#endif
if ((fd == -1) && (errno == ENOTDIR)) {
size_t parsed, len;
/*
* dirfsp does not point at a directory, try a
* freadlink.
*/
symlink_err = talloc_zero(mem_ctx, struct open_symlink_err);
if (symlink_err == NULL) {
goto nomem;
}
status = read_symlink_reparse(symlink_err,
dirfsp,
NULL,
&symlink_err->reparse);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("readlink_talloc failed: %s\n",
nt_errstr(status));
status = NT_STATUS_NOT_A_DIRECTORY;
goto fail;
}
parsed = rel_fname.base_name - path;
len = talloc_get_size(path);
symlink_err->unparsed = len - parsed;
symlink_err->st = dirfsp->fsp_name->st;
status = NT_STATUS_STOPPED_ON_SYMLINK;
goto fail;
}
@ -1094,11 +1152,8 @@ next:
}
fsp_set_fd(fsp, fd);
fsp->fsp_flags.is_directory = true; /* See O_DIRECTORY above */
ok = full_path_extend(&full_fname.base_name, rel_fname.base_name);
if (!ok) {
DBG_DEBUG("full_path_extend() failed\n");
goto nomem;
}
@ -1201,29 +1256,69 @@ fail:
dirfsp = NULL;
}
if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
*_symlink_err = symlink_err;
}
TALLOC_FREE(path);
return status;
}
NTSTATUS openat_pathref_dirfsp_nosymlink(TALLOC_CTX *mem_ctx,
NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
struct connection_struct *conn,
struct files_struct *dirfsp,
const char *path_in,
NTTIME twrp,
bool posix,
struct smb_filename **_smb_fname,
size_t *unparsed,
char **substitute)
struct open_symlink_err **_symlink_err)
{
NTSTATUS status = openat_pathref_fsp_nosymlink_internal(mem_ctx,
struct smb_filename *smb_fname = NULL;
struct open_symlink_err *symlink_err = NULL;
NTSTATUS status;
status = openat_pathref_fsp_nosymlink_internal(mem_ctx,
conn,
conn->cwd_fsp,
dirfsp,
path_in,
twrp,
posix,
_smb_fname,
unparsed,
substitute);
&smb_fname,
&symlink_err);
if (NT_STATUS_IS_OK(status) && S_ISLNK(smb_fname->st.st_ex_mode)) {
symlink_err = talloc_zero(mem_ctx, struct open_symlink_err);
if (symlink_err == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = read_symlink_reparse(symlink_err,
smb_fname->fsp,
NULL,
&symlink_err->reparse);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("read_symlink_reparse() failed: %s\n",
nt_errstr(status));
TALLOC_FREE(symlink_err);
return status;
}
symlink_err->st = smb_fname->st;
TALLOC_FREE(smb_fname);
status = NT_STATUS_STOPPED_ON_SYMLINK;
}
if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
*_symlink_err = symlink_err;
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
*_smb_fname = smb_fname;
return NT_STATUS_OK;
}
void smb_fname_fsp_unlink(struct smb_filename *smb_fname)

View File

@ -429,15 +429,23 @@ NTSTATUS openat_pathref_fsp(const struct files_struct *dirfsp,
NTSTATUS open_stream_pathref_fsp(
struct files_struct **_base_fsp,
struct smb_filename *smb_fname);
NTSTATUS openat_pathref_dirfsp_nosymlink(
TALLOC_CTX *mem_ctx,
struct symlink_reparse_struct;
struct open_symlink_err {
struct stat_ex st;
size_t unparsed;
struct symlink_reparse_struct *reparse;
};
NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
struct connection_struct *conn,
struct files_struct *dirfsp,
const char *path_in,
NTTIME twrp,
bool posix,
struct smb_filename **_smb_fname,
size_t *unparsed,
char **substitute);
struct open_symlink_err **_symlink_err);
NTSTATUS readlink_talloc(
TALLOC_CTX *mem_ctx,
struct files_struct *dirfsp,