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:
parent
eb2978f55c
commit
e9363926dc
@ -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,
|
||||
conn,
|
||||
dirname,
|
||||
0,
|
||||
posix,
|
||||
&smb_dirname,
|
||||
&unparsed,
|
||||
&substitute);
|
||||
status = openat_pathref_fsp_nosymlink(mem_ctx,
|
||||
conn,
|
||||
conn->cwd_fsp,
|
||||
dirname,
|
||||
0,
|
||||
posix,
|
||||
&smb_dirname,
|
||||
&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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
* O_NOFOLLOW|O_DIRECTORY results in
|
||||
* ENOTDIR instead of ELOOP.
|
||||
*
|
||||
* But we should be prepared to handle ELOOP too.
|
||||
*/
|
||||
if ((fd == -1) && (errno == ENOTDIR || errno == ELOOP)) {
|
||||
NTSTATUS orig_status = map_nt_error_from_unix(errno);
|
||||
#ifndef O_PATH
|
||||
if ((fd == -1) && (errno == ELOOP)) {
|
||||
int ret;
|
||||
|
||||
status = readlink_talloc(
|
||||
mem_ctx, dirfsp, &rel_fname, substitute);
|
||||
/*
|
||||
* openat() hit a symlink. With O_PATH we open the
|
||||
* symlink and get ENOTDIR in the next round, see
|
||||
* below.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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 {
|
||||
symlink_err = talloc_zero(mem_ctx, struct open_symlink_err);
|
||||
if (symlink_err == NULL) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
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,
|
||||
struct connection_struct *conn,
|
||||
const char *path_in,
|
||||
NTTIME twrp,
|
||||
bool posix,
|
||||
struct smb_filename **_smb_fname,
|
||||
size_t *unparsed,
|
||||
char **substitute)
|
||||
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,
|
||||
struct open_symlink_err **_symlink_err)
|
||||
{
|
||||
NTSTATUS status = openat_pathref_fsp_nosymlink_internal(mem_ctx,
|
||||
conn,
|
||||
conn->cwd_fsp,
|
||||
path_in,
|
||||
twrp,
|
||||
posix,
|
||||
_smb_fname,
|
||||
unparsed,
|
||||
substitute);
|
||||
return status;
|
||||
struct smb_filename *smb_fname = NULL;
|
||||
struct open_symlink_err *symlink_err = NULL;
|
||||
NTSTATUS status;
|
||||
|
||||
status = openat_pathref_fsp_nosymlink_internal(mem_ctx,
|
||||
conn,
|
||||
dirfsp,
|
||||
path_in,
|
||||
twrp,
|
||||
posix,
|
||||
&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)
|
||||
|
@ -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 connection_struct *conn,
|
||||
const char *path_in,
|
||||
NTTIME twrp,
|
||||
bool posix,
|
||||
struct smb_filename **_smb_fname,
|
||||
size_t *unparsed,
|
||||
char **substitute);
|
||||
|
||||
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,
|
||||
struct open_symlink_err **_symlink_err);
|
||||
NTSTATUS readlink_talloc(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct files_struct *dirfsp,
|
||||
|
Loading…
Reference in New Issue
Block a user