mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +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/smbd.h"
|
||||||
#include "smbd/globals.h"
|
#include "smbd/globals.h"
|
||||||
#include "lib/util/memcache.h"
|
#include "lib/util/memcache.h"
|
||||||
|
#include "libcli/smb/reparse_symlink.h"
|
||||||
|
|
||||||
uint32_t ucf_flags_from_smb_request(struct smb_request *req)
|
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,
|
posix ? SMB_FILENAME_POSIX_PATH : 0,
|
||||||
&smb_dirname);
|
&smb_dirname);
|
||||||
} else {
|
} else {
|
||||||
char *substitute = NULL;
|
struct open_symlink_err *symlink_err = NULL;
|
||||||
size_t unparsed = 0;
|
|
||||||
|
|
||||||
status = normalize_filename_case(conn, dirname, ucf_flags);
|
status = normalize_filename_case(conn, dirname, ucf_flags);
|
||||||
if (!NT_STATUS_IS_OK(status)) {
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
@ -1106,25 +1106,36 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = openat_pathref_dirfsp_nosymlink(
|
status = openat_pathref_fsp_nosymlink(mem_ctx,
|
||||||
mem_ctx,
|
|
||||||
conn,
|
conn,
|
||||||
|
conn->cwd_fsp,
|
||||||
dirname,
|
dirname,
|
||||||
0,
|
0,
|
||||||
posix,
|
posix,
|
||||||
&smb_dirname,
|
&smb_dirname,
|
||||||
&unparsed,
|
&symlink_err);
|
||||||
&substitute);
|
|
||||||
|
|
||||||
if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
|
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);
|
if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
|
||||||
size_t dirname_len = strlen(dirname);
|
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);
|
SMB_ASSERT(name_in_len >= dirname_len);
|
||||||
|
|
||||||
*_substitute = substitute;
|
*_substitute = talloc_move(
|
||||||
*_unparsed = unparsed + (name_in_len - dirname_len);
|
mem_ctx,
|
||||||
|
&symlink_err->reparse->substitute_name);
|
||||||
|
*_unparsed = symlink_err->unparsed +
|
||||||
|
(name_in_len - dirname_len);
|
||||||
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -1136,11 +1147,6 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|||||||
nt_errstr(status));
|
nt_errstr(status));
|
||||||
TALLOC_FREE(dirname);
|
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)) {
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
|
||||||
/*
|
/*
|
||||||
* Except ACCESS_DENIED, everything else leads
|
* Except ACCESS_DENIED, everything else leads
|
||||||
@ -1156,6 +1162,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|||||||
status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
|
status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
smb_dirname->fsp->fsp_flags.is_directory = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only look at bad last component values
|
* Only look at bad last component values
|
||||||
|
@ -808,8 +808,7 @@ openat_pathref_fsp_nosymlink_internal(TALLOC_CTX *mem_ctx,
|
|||||||
NTTIME twrp,
|
NTTIME twrp,
|
||||||
bool posix,
|
bool posix,
|
||||||
struct smb_filename **_smb_fname,
|
struct smb_filename **_smb_fname,
|
||||||
size_t *unparsed,
|
struct open_symlink_err **_symlink_err)
|
||||||
char **substitute)
|
|
||||||
{
|
{
|
||||||
struct files_struct *dirfsp = in_dirfsp;
|
struct files_struct *dirfsp = in_dirfsp;
|
||||||
struct smb_filename full_fname = {
|
struct smb_filename full_fname = {
|
||||||
@ -823,13 +822,14 @@ openat_pathref_fsp_nosymlink_internal(TALLOC_CTX *mem_ctx,
|
|||||||
.flags = full_fname.flags,
|
.flags = full_fname.flags,
|
||||||
};
|
};
|
||||||
struct smb_filename *result = NULL;
|
struct smb_filename *result = NULL;
|
||||||
|
struct open_symlink_err *symlink_err = NULL;
|
||||||
struct files_struct *fsp = NULL;
|
struct files_struct *fsp = NULL;
|
||||||
char *path = NULL, *next = NULL;
|
char *path = NULL, *next = NULL;
|
||||||
bool case_sensitive, ok;
|
bool case_sensitive, ok, is_toplevel;
|
||||||
int fd;
|
int fd;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
struct vfs_open_how how = {
|
struct vfs_open_how how = {
|
||||||
.flags = O_NOFOLLOW|O_DIRECTORY,
|
.flags = O_NOFOLLOW | O_NONBLOCK,
|
||||||
.mode = 0,
|
.mode = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -868,7 +868,12 @@ openat_pathref_fsp_nosymlink_internal(TALLOC_CTX *mem_ctx,
|
|||||||
#endif
|
#endif
|
||||||
#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) {
|
if (full_fname.base_name == NULL) {
|
||||||
DBG_DEBUG("talloc_strdup() failed\n");
|
DBG_DEBUG("talloc_strdup() failed\n");
|
||||||
goto nomem;
|
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);
|
fd = SMB_VFS_OPENAT(conn, dirfsp, &rel_fname, fsp, &how);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
fsp_set_fd(fsp, fd);
|
fsp_set_fd(fsp, fd);
|
||||||
TALLOC_FREE(full_fname.base_name);
|
ok = full_path_extend(&full_fname.base_name,
|
||||||
full_fname = rel_fname;
|
rel_fname.base_name);
|
||||||
|
if (!ok) {
|
||||||
|
goto nomem;
|
||||||
|
}
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1035,54 +1043,104 @@ next:
|
|||||||
&how);
|
&how);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef O_PATH
|
||||||
|
if ((fd == -1) && (errno == ELOOP)) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* O_NOFOLLOW|O_DIRECTORY results in
|
* openat() hit a symlink. With O_PATH we open the
|
||||||
* ENOTDIR instead of ELOOP.
|
* symlink and get ENOTDIR in the next round, see
|
||||||
*
|
* below.
|
||||||
* 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);
|
|
||||||
|
|
||||||
status = readlink_talloc(
|
symlink_err = talloc_zero(mem_ctx, struct open_symlink_err);
|
||||||
mem_ctx, dirfsp, &rel_fname, substitute);
|
if (symlink_err == NULL) {
|
||||||
|
goto nomem;
|
||||||
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 {
|
|
||||||
|
|
||||||
|
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",
|
DBG_DEBUG("readlink_talloc failed: %s\n",
|
||||||
nt_errstr(status));
|
nt_errstr(status));
|
||||||
/*
|
goto fail;
|
||||||
* Restore the error status from SMB_VFS_OPENAT()
|
|
||||||
*/
|
|
||||||
status = orig_status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1094,11 +1152,8 @@ next:
|
|||||||
}
|
}
|
||||||
fsp_set_fd(fsp, fd);
|
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);
|
ok = full_path_extend(&full_fname.base_name, rel_fname.base_name);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
DBG_DEBUG("full_path_extend() failed\n");
|
|
||||||
goto nomem;
|
goto nomem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1201,29 +1256,69 @@ fail:
|
|||||||
dirfsp = NULL;
|
dirfsp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
|
||||||
|
*_symlink_err = symlink_err;
|
||||||
|
}
|
||||||
|
|
||||||
TALLOC_FREE(path);
|
TALLOC_FREE(path);
|
||||||
return status;
|
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 connection_struct *conn,
|
||||||
|
struct files_struct *dirfsp,
|
||||||
const char *path_in,
|
const char *path_in,
|
||||||
NTTIME twrp,
|
NTTIME twrp,
|
||||||
bool posix,
|
bool posix,
|
||||||
struct smb_filename **_smb_fname,
|
struct smb_filename **_smb_fname,
|
||||||
size_t *unparsed,
|
struct open_symlink_err **_symlink_err)
|
||||||
char **substitute)
|
|
||||||
{
|
{
|
||||||
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,
|
||||||
conn->cwd_fsp,
|
dirfsp,
|
||||||
path_in,
|
path_in,
|
||||||
twrp,
|
twrp,
|
||||||
posix,
|
posix,
|
||||||
_smb_fname,
|
&smb_fname,
|
||||||
unparsed,
|
&symlink_err);
|
||||||
substitute);
|
|
||||||
|
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;
|
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)
|
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(
|
NTSTATUS open_stream_pathref_fsp(
|
||||||
struct files_struct **_base_fsp,
|
struct files_struct **_base_fsp,
|
||||||
struct smb_filename *smb_fname);
|
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 connection_struct *conn,
|
||||||
|
struct files_struct *dirfsp,
|
||||||
const char *path_in,
|
const char *path_in,
|
||||||
NTTIME twrp,
|
NTTIME twrp,
|
||||||
bool posix,
|
bool posix,
|
||||||
struct smb_filename **_smb_fname,
|
struct smb_filename **_smb_fname,
|
||||||
size_t *unparsed,
|
struct open_symlink_err **_symlink_err);
|
||||||
char **substitute);
|
|
||||||
NTSTATUS readlink_talloc(
|
NTSTATUS readlink_talloc(
|
||||||
TALLOC_CTX *mem_ctx,
|
TALLOC_CTX *mem_ctx,
|
||||||
struct files_struct *dirfsp,
|
struct files_struct *dirfsp,
|
||||||
|
Loading…
Reference in New Issue
Block a user