mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
smbd: Simplify openat_pathref_fsp_case_insensitive()
This is more lines of code, but it's still a simplification. With this patch we don't call the full openat_pathref_fsp() anymore when looking up the last component in filename_convert_dirfsp(), instead we do the direct SMB_VFS_OPENAT(). We don't need the whole complexity of non_widelink_open() for this case, we do know that we have a real non-cwd dirfsp. The other big change that is not obvious just from looking at the patch: This removes the special case for looking up posix symlinks. Before this patch, filename_convert_dirfsp() returned a proper smb_filename but without an attached fsp when a smb1 posix client hits a symlink. This caused all sorts of special case code everywhere. For example smbd_do_qfilepathinfo() needs to cover both cases just for the smb1 posix symlink case. This special-case handling can go now. We can do the path lookup in the smb1-only qpathinfo code and call into the common code with a proper fsp. When hitting a symlink and with O_PATH available, we'll get the symlink opened with an O_PATH fd. Without O_PATH we obviously can't do that, there we get fd=-1 and an indication that we don't have the procfd fallback around. Why all this? I want to present FIFOs (and eventually symlinks) as reparse points as the very next step. Without this patch, there is no real unified way to get the file attributes from disk. Now we can use the proper logic of fdos_mode() everywhere and not rely on special cases for fsp==NULL. This patch also changes some error codes for smb1 posix extensions. I chose to just change the test instead of going after each and every change. As long as we do get an error, I'm willing to accept that we slightly change error path behaviour for this deprecated code. And, I tried to split this up into smaller patches but I failed. Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Ralph Boehme <slow@samba.org>
This commit is contained in:
parent
ab56379c22
commit
8d00b0e664
@ -389,7 +389,7 @@ static int widelinks_openat(vfs_handle_struct *handle,
|
|||||||
fsp->fsp_name->st = full_fname->st;
|
fsp->fsp_name->st = full_fname->st;
|
||||||
}
|
}
|
||||||
TALLOC_FREE(full_fname);
|
TALLOC_FREE(full_fname);
|
||||||
errno = ENOENT;
|
errno = ELOOP;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -194,13 +194,13 @@ test_symlink_traversal_SMB1_posix_onename()
|
|||||||
# ls commands.
|
# ls commands.
|
||||||
#
|
#
|
||||||
smbclient_expect_error "ls" "$name" "" "NT_STATUS_OK" || return 1
|
smbclient_expect_error "ls" "$name" "" "NT_STATUS_OK" || return 1
|
||||||
smbclient_expect_error "ls" "$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
|
smbclient_expect_error "ls" "$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
|
||||||
smbclient_expect_error "ls" "$name/*" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
|
smbclient_expect_error "ls" "$name/*" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
|
||||||
smbclient_expect_error "ls" "$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
|
smbclient_expect_error "ls" "$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
|
||||||
# Now in subdirectory emptydir
|
# Now in subdirectory emptydir
|
||||||
smbclient_expect_error "ls" "emptydir/$name" "" "NT_STATUS_OK" || return 1
|
smbclient_expect_error "ls" "emptydir/$name" "" "NT_STATUS_OK" || return 1
|
||||||
smbclient_expect_error "ls" "emptydir/$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
|
smbclient_expect_error "ls" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
|
||||||
smbclient_expect_error "ls" "emptydir/$name/*" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
|
smbclient_expect_error "ls" "emptydir/$name/*" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
|
||||||
smbclient_expect_error "ls" "emptydir/$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
|
smbclient_expect_error "ls" "emptydir/$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
|
||||||
#
|
#
|
||||||
# SMB1+POSIX stat commands. All symlinks can be stat'ed.
|
# SMB1+POSIX stat commands. All symlinks can be stat'ed.
|
||||||
@ -211,9 +211,9 @@ test_symlink_traversal_SMB1_posix_onename()
|
|||||||
# del commands. Under SMB1+POSIX we can legitimately delete symlinks, so don't
|
# del commands. Under SMB1+POSIX we can legitimately delete symlinks, so don't
|
||||||
# try and delete symlink targets, we need them for the later tests.
|
# try and delete symlink targets, we need them for the later tests.
|
||||||
#
|
#
|
||||||
smbclient_expect_error "del" "$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
|
smbclient_expect_error "del" "$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
|
||||||
# Now in subdirectory emptydir
|
# Now in subdirectory emptydir
|
||||||
smbclient_expect_error "del" "emptydir/$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
|
smbclient_expect_error "del" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
|
||||||
|
|
||||||
if [ "$do_rename" = "do rename" ]; then
|
if [ "$do_rename" = "do rename" ]; then
|
||||||
#
|
#
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
#include "fake_file.h"
|
#include "fake_file.h"
|
||||||
#include "smbd/smbd.h"
|
#include "smbd/smbd.h"
|
||||||
#include "smbd/globals.h"
|
#include "smbd/globals.h"
|
||||||
#include "lib/util/memcache.h"
|
|
||||||
#include "libcli/smb/reparse.h"
|
#include "libcli/smb/reparse.h"
|
||||||
|
|
||||||
uint32_t ucf_flags_from_smb_request(struct smb_request *req)
|
uint32_t ucf_flags_from_smb_request(struct smb_request *req)
|
||||||
@ -640,155 +639,6 @@ static NTSTATUS filename_convert_normalize_new(
|
|||||||
return NT_STATUS_OK;
|
return NT_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Open smb_fname_rel->fsp as a pathref fsp with a case insensitive
|
|
||||||
* fallback using GETREALFILENAME_CACHE and get_real_filename_at() if
|
|
||||||
* the first attempt based on the filename sent by the client gives
|
|
||||||
* ENOENT.
|
|
||||||
*/
|
|
||||||
static NTSTATUS openat_pathref_fsp_case_insensitive(
|
|
||||||
struct files_struct *dirfsp,
|
|
||||||
struct smb_filename *smb_fname_rel,
|
|
||||||
uint32_t ucf_flags)
|
|
||||||
{
|
|
||||||
const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
|
|
||||||
DATA_BLOB cache_key = { .data = NULL, };
|
|
||||||
char *found_name = NULL;
|
|
||||||
NTSTATUS status;
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
SET_STAT_INVALID(smb_fname_rel->st);
|
|
||||||
|
|
||||||
/* Check veto files - only looks at last component. */
|
|
||||||
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
|
|
||||||
DBG_DEBUG("veto files rejecting last component %s\n",
|
|
||||||
smb_fname_str_dbg(smb_fname_rel));
|
|
||||||
return NT_STATUS_NETWORK_OPEN_RESTRICTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = openat_pathref_fsp(dirfsp, smb_fname_rel);
|
|
||||||
|
|
||||||
if (NT_STATUS_IS_OK(status)) {
|
|
||||||
return NT_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VALID_STAT(smb_fname_rel->st)) {
|
|
||||||
/*
|
|
||||||
* We got an error although the object existed. Might
|
|
||||||
* be a symlink we don't want.
|
|
||||||
*/
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
|
|
||||||
/*
|
|
||||||
* Only retry on ENOENT
|
|
||||||
*/
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (posix || dirfsp->conn->case_sensitive) {
|
|
||||||
/*
|
|
||||||
* Only return case insensitive if required
|
|
||||||
*/
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lp_stat_cache()) {
|
|
||||||
char *base_name = smb_fname_rel->base_name;
|
|
||||||
char *original_relname = NULL;
|
|
||||||
DATA_BLOB value = { .data = NULL };
|
|
||||||
|
|
||||||
ok = get_real_filename_cache_key(
|
|
||||||
talloc_tos(), dirfsp, base_name, &cache_key);
|
|
||||||
if (!ok) {
|
|
||||||
/*
|
|
||||||
* probably ENOMEM, just bail
|
|
||||||
*/
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
DO_PROFILE_INC(statcache_lookups);
|
|
||||||
|
|
||||||
ok = memcache_lookup(
|
|
||||||
NULL, GETREALFILENAME_CACHE, cache_key, &value);
|
|
||||||
if (!ok) {
|
|
||||||
DO_PROFILE_INC(statcache_misses);
|
|
||||||
goto lookup;
|
|
||||||
}
|
|
||||||
DO_PROFILE_INC(statcache_hits);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For the "new filename" case we need to preserve the
|
|
||||||
* capitalization the client sent us, see
|
|
||||||
* https://bugzilla.samba.org/show_bug.cgi?id=15481
|
|
||||||
*/
|
|
||||||
original_relname = smb_fname_rel->base_name;
|
|
||||||
|
|
||||||
smb_fname_rel->base_name = talloc_memdup(
|
|
||||||
smb_fname_rel, value.data, value.length);
|
|
||||||
if (smb_fname_rel->base_name == NULL) {
|
|
||||||
TALLOC_FREE(cache_key.data);
|
|
||||||
return NT_STATUS_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
|
|
||||||
DBG_DEBUG("veto files rejecting last component %s\n",
|
|
||||||
smb_fname_str_dbg(smb_fname_rel));
|
|
||||||
TALLOC_FREE(cache_key.data);
|
|
||||||
return NT_STATUS_NETWORK_OPEN_RESTRICTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = openat_pathref_fsp(dirfsp, smb_fname_rel);
|
|
||||||
if (NT_STATUS_IS_OK(status)) {
|
|
||||||
TALLOC_FREE(cache_key.data);
|
|
||||||
TALLOC_FREE(original_relname);
|
|
||||||
return NT_STATUS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcache_delete(NULL, GETREALFILENAME_CACHE, cache_key);
|
|
||||||
TALLOC_FREE(smb_fname_rel->base_name);
|
|
||||||
smb_fname_rel->base_name = original_relname;
|
|
||||||
}
|
|
||||||
|
|
||||||
lookup:
|
|
||||||
status = get_real_filename_at(
|
|
||||||
dirfsp, smb_fname_rel->base_name, smb_fname_rel, &found_name);
|
|
||||||
if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
|
|
||||||
(ucf_flags & UCF_PREP_CREATEFILE)) {
|
|
||||||
/*
|
|
||||||
* dropbox
|
|
||||||
*/
|
|
||||||
status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NT_STATUS_IS_OK(status)) {
|
|
||||||
TALLOC_FREE(smb_fname_rel->base_name);
|
|
||||||
smb_fname_rel->base_name = found_name;
|
|
||||||
|
|
||||||
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
|
|
||||||
DBG_DEBUG("veto files rejecting last component %s\n",
|
|
||||||
smb_fname_str_dbg(smb_fname_rel));
|
|
||||||
return NT_STATUS_NETWORK_OPEN_RESTRICTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = openat_pathref_fsp(dirfsp, smb_fname_rel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NT_STATUS_IS_OK(status) && (cache_key.data != NULL)) {
|
|
||||||
DATA_BLOB value = {
|
|
||||||
.data = (uint8_t *)smb_fname_rel->base_name,
|
|
||||||
.length = strlen(smb_fname_rel->base_name) + 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
memcache_add(NULL, GETREALFILENAME_CACHE, cache_key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
TALLOC_FREE(cache_key.data);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *previous_slash(const char *name_in, const char *slash)
|
static const char *previous_slash(const char *name_in, const char *slash)
|
||||||
{
|
{
|
||||||
const char *prev = NULL;
|
const char *prev = NULL;
|
||||||
@ -936,6 +786,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|||||||
struct smb_filename *smb_dirname = NULL;
|
struct smb_filename *smb_dirname = NULL;
|
||||||
struct smb_filename *smb_fname_rel = NULL;
|
struct smb_filename *smb_fname_rel = NULL;
|
||||||
struct smb_filename *smb_fname = NULL;
|
struct smb_filename *smb_fname = NULL;
|
||||||
|
struct open_symlink_err *symlink_err = NULL;
|
||||||
const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
|
const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
|
||||||
char *dirname = NULL;
|
char *dirname = NULL;
|
||||||
const char *fname_rel = NULL;
|
const char *fname_rel = NULL;
|
||||||
@ -1021,8 +872,6 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|||||||
posix ? SMB_FILENAME_POSIX_PATH : 0,
|
posix ? SMB_FILENAME_POSIX_PATH : 0,
|
||||||
&smb_dirname);
|
&smb_dirname);
|
||||||
} else {
|
} else {
|
||||||
struct open_symlink_err *symlink_err = NULL;
|
|
||||||
|
|
||||||
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)) {
|
||||||
DBG_ERR("normalize_filename_case %s failed: %s\n",
|
DBG_ERR("normalize_filename_case %s failed: %s\n",
|
||||||
@ -1043,14 +892,6 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|||||||
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, dirname_len;
|
||||||
|
|
||||||
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);
|
name_in_len = strlen(name_in);
|
||||||
dirname_len = strlen(dirname);
|
dirname_len = strlen(dirname);
|
||||||
|
|
||||||
@ -1143,45 +984,27 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = openat_pathref_fsp_case_insensitive(
|
status = openat_pathref_fsp_lcomp(smb_dirname->fsp,
|
||||||
smb_dirname->fsp, smb_fname_rel, ucf_flags);
|
smb_fname_rel,
|
||||||
|
ucf_flags);
|
||||||
|
|
||||||
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
|
if (NT_STATUS_IS_OK(status) && S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
|
||||||
VALID_STAT(smb_fname_rel->st) &&
|
|
||||||
S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we're on an MSDFS share, see if this is
|
* Upper layers might need the link target. Here we
|
||||||
* an MSDFS link.
|
* still have the relname around, get the symlink err.
|
||||||
*/
|
*/
|
||||||
if (lp_host_msdfs() &&
|
status = create_open_symlink_err(mem_ctx,
|
||||||
lp_msdfs_root(SNUM(conn)) &&
|
smb_dirname->fsp,
|
||||||
is_msdfs_link(smb_dirname->fsp, smb_fname_rel))
|
smb_fname_rel,
|
||||||
{
|
&symlink_err);
|
||||||
status = NT_STATUS_PATH_NOT_COVERED;
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
DBG_DEBUG("Could not read symlink for %s: %s\n",
|
||||||
|
smb_fname_str_dbg(
|
||||||
|
smb_fname_rel->fsp->fsp_name),
|
||||||
|
nt_errstr(status));
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(WITH_SMB1SERVER)
|
|
||||||
/*
|
|
||||||
* In SMB1 posix mode, if this is a symlink,
|
|
||||||
* allow access to the name with a NULL smb_fname->fsp.
|
|
||||||
*/
|
|
||||||
if (ucf_flags & UCF_LCOMP_LNK_OK) {
|
|
||||||
SMB_ASSERT(smb_fname_rel->fsp == NULL);
|
|
||||||
SMB_ASSERT(streamname == NULL);
|
|
||||||
|
|
||||||
smb_fname = full_path_from_dirfsp_atname(
|
|
||||||
mem_ctx,
|
|
||||||
smb_dirname->fsp,
|
|
||||||
smb_fname_rel);
|
|
||||||
if (smb_fname == NULL) {
|
|
||||||
status = NT_STATUS_NO_MEMORY;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
|
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
|
||||||
@ -1314,6 +1137,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|||||||
done:
|
done:
|
||||||
*_dirfsp = smb_dirname->fsp;
|
*_dirfsp = smb_dirname->fsp;
|
||||||
*_smb_fname = smb_fname;
|
*_smb_fname = smb_fname;
|
||||||
|
*_symlink_err = symlink_err;
|
||||||
|
|
||||||
smb_fname_fsp_unlink(smb_fname_rel);
|
smb_fname_fsp_unlink(smb_fname_rel);
|
||||||
TALLOC_FREE(smb_fname_rel);
|
TALLOC_FREE(smb_fname_rel);
|
||||||
@ -1366,10 +1190,34 @@ next:
|
|||||||
_smb_fname,
|
_smb_fname,
|
||||||
&symlink_err);
|
&symlink_err);
|
||||||
|
|
||||||
|
if (NT_STATUS_IS_OK(status) && S_ISLNK((*_smb_fname)->st.st_ex_mode)) {
|
||||||
|
/*
|
||||||
|
* lcomp is a symlink
|
||||||
|
*/
|
||||||
|
if (ucf_flags & UCF_LCOMP_LNK_OK) {
|
||||||
|
TALLOC_FREE(symlink_err);
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
close_file_free(NULL, _dirfsp, ERROR_CLOSE);
|
||||||
|
status = NT_STATUS_STOPPED_ON_SYMLINK;
|
||||||
|
}
|
||||||
|
|
||||||
if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're on an MSDFS share, see if this is
|
||||||
|
* an MSDFS link.
|
||||||
|
*/
|
||||||
|
if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
|
||||||
|
strnequal(symlink_err->reparse->substitute_name, "msdfs:", 6))
|
||||||
|
{
|
||||||
|
TALLOC_FREE(*_smb_fname);
|
||||||
|
TALLOC_FREE(symlink_err);
|
||||||
|
return NT_STATUS_PATH_NOT_COVERED;
|
||||||
|
}
|
||||||
|
|
||||||
if (!lp_follow_symlinks(SNUM(conn))) {
|
if (!lp_follow_symlinks(SNUM(conn))) {
|
||||||
status = (symlink_err->unparsed == 0)
|
status = (symlink_err->unparsed == 0)
|
||||||
? NT_STATUS_OBJECT_NAME_NOT_FOUND
|
? NT_STATUS_OBJECT_NAME_NOT_FOUND
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "util_tdb.h"
|
#include "util_tdb.h"
|
||||||
#include "lib/util/bitmap.h"
|
#include "lib/util/bitmap.h"
|
||||||
#include "lib/util/strv.h"
|
#include "lib/util/strv.h"
|
||||||
|
#include "lib/util/memcache.h"
|
||||||
#include "libcli/smb/reparse.h"
|
#include "libcli/smb/reparse.h"
|
||||||
|
|
||||||
#define FILE_HANDLE_OFFSET 0x1000
|
#define FILE_HANDLE_OFFSET 0x1000
|
||||||
@ -831,6 +832,135 @@ NTSTATUS create_open_symlink_err(TALLOC_CTX *mem_ctx,
|
|||||||
return NT_STATUS_OK;
|
return NT_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int smb_vfs_openat_ci(TALLOC_CTX *mem_ctx,
|
||||||
|
bool case_sensitive,
|
||||||
|
struct connection_struct *conn,
|
||||||
|
struct files_struct *dirfsp,
|
||||||
|
struct smb_filename *smb_fname_rel,
|
||||||
|
files_struct *fsp,
|
||||||
|
const struct vfs_open_how *how)
|
||||||
|
{
|
||||||
|
char *orig_base_name = smb_fname_rel->base_name;
|
||||||
|
DATA_BLOB cache_key = {
|
||||||
|
.data = NULL,
|
||||||
|
};
|
||||||
|
DATA_BLOB cache_value = {
|
||||||
|
.data = NULL,
|
||||||
|
};
|
||||||
|
NTSTATUS status;
|
||||||
|
int fd;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
fd = SMB_VFS_OPENAT(conn, dirfsp, smb_fname_rel, fsp, how);
|
||||||
|
if ((fd >= 0) || case_sensitive) {
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lp_stat_cache()) {
|
||||||
|
goto lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = get_real_filename_cache_key(mem_ctx,
|
||||||
|
dirfsp,
|
||||||
|
orig_base_name,
|
||||||
|
&cache_key);
|
||||||
|
if (!ok) {
|
||||||
|
/*
|
||||||
|
* probably ENOMEM, just bail
|
||||||
|
*/
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DO_PROFILE_INC(statcache_lookups);
|
||||||
|
|
||||||
|
ok = memcache_lookup(NULL,
|
||||||
|
GETREALFILENAME_CACHE,
|
||||||
|
cache_key,
|
||||||
|
&cache_value);
|
||||||
|
if (!ok) {
|
||||||
|
DO_PROFILE_INC(statcache_misses);
|
||||||
|
goto lookup;
|
||||||
|
}
|
||||||
|
DO_PROFILE_INC(statcache_hits);
|
||||||
|
|
||||||
|
smb_fname_rel->base_name = talloc_strndup(mem_ctx,
|
||||||
|
(char *)cache_value.data,
|
||||||
|
cache_value.length);
|
||||||
|
if (smb_fname_rel->base_name == NULL) {
|
||||||
|
TALLOC_FREE(cache_key.data);
|
||||||
|
smb_fname_rel->base_name = orig_base_name;
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
|
||||||
|
DBG_DEBUG("veto files rejecting last component %s\n",
|
||||||
|
smb_fname_str_dbg(smb_fname_rel));
|
||||||
|
TALLOC_FREE(cache_key.data);
|
||||||
|
smb_fname_rel->base_name = orig_base_name;
|
||||||
|
errno = EPERM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = SMB_VFS_OPENAT(conn, dirfsp, smb_fname_rel, fsp, how);
|
||||||
|
if (fd >= 0) {
|
||||||
|
TALLOC_FREE(cache_key.data);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcache_delete(NULL, GETREALFILENAME_CACHE, cache_key);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For the "new filename" case we need to preserve the
|
||||||
|
* capitalization the client sent us, see
|
||||||
|
* https://bugzilla.samba.org/show_bug.cgi?id=15481
|
||||||
|
*/
|
||||||
|
TALLOC_FREE(smb_fname_rel->base_name);
|
||||||
|
smb_fname_rel->base_name = orig_base_name;
|
||||||
|
|
||||||
|
lookup:
|
||||||
|
|
||||||
|
status = get_real_filename_at(dirfsp,
|
||||||
|
orig_base_name,
|
||||||
|
mem_ctx,
|
||||||
|
&smb_fname_rel->base_name);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
DBG_DEBUG("get_real_filename_at() failed: %s\n",
|
||||||
|
nt_errstr(status));
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_VETO_PATH(conn, smb_fname_rel->base_name)) {
|
||||||
|
DBG_DEBUG("found veto files path component "
|
||||||
|
"%s => %s\n",
|
||||||
|
orig_base_name,
|
||||||
|
smb_fname_rel->base_name);
|
||||||
|
TALLOC_FREE(smb_fname_rel->base_name);
|
||||||
|
smb_fname_rel->base_name = orig_base_name;
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = SMB_VFS_OPENAT(conn, dirfsp, smb_fname_rel, fsp, how);
|
||||||
|
|
||||||
|
if ((fd >= 0) && (cache_key.data != NULL)) {
|
||||||
|
DATA_BLOB value = {
|
||||||
|
.data = (uint8_t *)smb_fname_rel->base_name,
|
||||||
|
.length = strlen(smb_fname_rel->base_name) + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
memcache_add(NULL, GETREALFILENAME_CACHE, cache_key, value);
|
||||||
|
TALLOC_FREE(cache_key.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS openat_pathref_fsp_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 *in_dirfsp,
|
struct files_struct *in_dirfsp,
|
||||||
@ -855,7 +985,7 @@ NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
|
|||||||
struct open_symlink_err *symlink_err = 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, is_toplevel;
|
bool ok, is_toplevel;
|
||||||
int fd;
|
int fd;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
struct vfs_open_how how = {
|
struct vfs_open_how how = {
|
||||||
@ -1030,48 +1160,13 @@ NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
|
|||||||
next:
|
next:
|
||||||
next = strv_next(path, rel_fname.base_name);
|
next = strv_next(path, rel_fname.base_name);
|
||||||
|
|
||||||
fd = SMB_VFS_OPENAT(
|
fd = smb_vfs_openat_ci(talloc_tos(),
|
||||||
conn,
|
posix || conn->case_sensitive,
|
||||||
dirfsp,
|
conn,
|
||||||
&rel_fname,
|
dirfsp,
|
||||||
fsp,
|
&rel_fname,
|
||||||
&how);
|
fsp,
|
||||||
|
&how);
|
||||||
case_sensitive = (posix || conn->case_sensitive);
|
|
||||||
|
|
||||||
if ((fd == -1) && (errno == ENOENT) && !case_sensitive) {
|
|
||||||
const char *orig_base_name = rel_fname.base_name;
|
|
||||||
|
|
||||||
status = get_real_filename_at(
|
|
||||||
dirfsp,
|
|
||||||
rel_fname.base_name,
|
|
||||||
talloc_tos(),
|
|
||||||
&rel_fname.base_name);
|
|
||||||
|
|
||||||
if (!NT_STATUS_IS_OK(status)) {
|
|
||||||
DBG_DEBUG("get_real_filename_at failed: %s\n",
|
|
||||||
nt_errstr(status));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Name might have been demangled - check veto files. */
|
|
||||||
if (IS_VETO_PATH(conn, rel_fname.base_name)) {
|
|
||||||
DBG_DEBUG("%s contains veto files path component "
|
|
||||||
"%s => %s\n",
|
|
||||||
path_in,
|
|
||||||
orig_base_name,
|
|
||||||
rel_fname.base_name);
|
|
||||||
status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = SMB_VFS_OPENAT(
|
|
||||||
conn,
|
|
||||||
dirfsp,
|
|
||||||
&rel_fname,
|
|
||||||
fsp,
|
|
||||||
&how);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef O_PATH
|
#ifndef O_PATH
|
||||||
if ((fd == -1) && (errno == ELOOP)) {
|
if ((fd == -1) && (errno == ELOOP)) {
|
||||||
@ -1304,6 +1399,162 @@ fail:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open smb_fname_rel->fsp as a pathref fsp with a case insensitive
|
||||||
|
* fallback using GETREALFILENAME_CACHE and get_real_filename_at() if
|
||||||
|
* the first attempt based on the filename sent by the client gives
|
||||||
|
* ENOENT.
|
||||||
|
*/
|
||||||
|
NTSTATUS openat_pathref_fsp_lcomp(struct files_struct *dirfsp,
|
||||||
|
struct smb_filename *smb_fname_rel,
|
||||||
|
uint32_t ucf_flags)
|
||||||
|
{
|
||||||
|
struct connection_struct *conn = dirfsp->conn;
|
||||||
|
const char *orig_rel_base_name = smb_fname_rel->base_name;
|
||||||
|
struct files_struct *fsp = NULL;
|
||||||
|
struct smb_filename *full_fname = NULL;
|
||||||
|
struct vfs_open_how how = {
|
||||||
|
.flags = O_RDONLY | O_NONBLOCK | O_NOFOLLOW,
|
||||||
|
};
|
||||||
|
NTSTATUS status;
|
||||||
|
int ret, fd;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure we don't need of the all the magic in
|
||||||
|
* openat_pathref_fsp() with regards non_widelink_open etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
SMB_ASSERT((smb_fname_rel->fsp == NULL) &&
|
||||||
|
(dirfsp != dirfsp->conn->cwd_fsp) &&
|
||||||
|
(strchr_m(smb_fname_rel->base_name, '/') == NULL) &&
|
||||||
|
!is_named_stream(smb_fname_rel));
|
||||||
|
|
||||||
|
SET_STAT_INVALID(smb_fname_rel->st);
|
||||||
|
|
||||||
|
/* Check veto files - only looks at last component. */
|
||||||
|
if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
|
||||||
|
DBG_DEBUG("veto files rejecting last component %s\n",
|
||||||
|
smb_fname_str_dbg(smb_fname_rel));
|
||||||
|
return NT_STATUS_NETWORK_OPEN_RESTRICTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = fsp_new(conn, conn, &fsp);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
DBG_DEBUG("fsp_new() failed: %s\n", nt_errstr(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetTimeOfDay(&fsp->open_time);
|
||||||
|
fsp_set_gen_id(fsp);
|
||||||
|
ZERO_STRUCT(conn->sconn->fsp_fi_cache);
|
||||||
|
|
||||||
|
fsp->fsp_flags.is_pathref = true;
|
||||||
|
|
||||||
|
full_fname = full_path_from_dirfsp_atname(conn, dirfsp, smb_fname_rel);
|
||||||
|
if (full_fname == NULL) {
|
||||||
|
DBG_DEBUG("full_path_from_dirfsp_atname(%s/%s) failed\n",
|
||||||
|
dirfsp->fsp_name->base_name,
|
||||||
|
smb_fname_rel->base_name);
|
||||||
|
file_free(NULL, fsp);
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = fsp_attach_smb_fname(fsp, &full_fname);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
DBG_DEBUG("fsp_attach_smb_fname(fsp, %s) failed: %s\n",
|
||||||
|
smb_fname_str_dbg(full_fname),
|
||||||
|
nt_errstr(status));
|
||||||
|
file_free(NULL, fsp);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = smb_vfs_openat_ci(smb_fname_rel,
|
||||||
|
(ucf_flags & UCF_POSIX_PATHNAMES) ||
|
||||||
|
conn->case_sensitive,
|
||||||
|
conn,
|
||||||
|
dirfsp,
|
||||||
|
smb_fname_rel,
|
||||||
|
fsp,
|
||||||
|
&how);
|
||||||
|
|
||||||
|
if ((fd == -1) && (errno == ENOENT)) {
|
||||||
|
status = map_nt_error_from_unix(errno);
|
||||||
|
DBG_DEBUG("smb_vfs_openat(%s/%s) failed: %s\n",
|
||||||
|
dirfsp->fsp_name->base_name,
|
||||||
|
smb_fname_rel->base_name,
|
||||||
|
strerror(errno));
|
||||||
|
file_free(NULL, fsp);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smb_fname_rel->base_name != orig_rel_base_name) {
|
||||||
|
struct smb_filename new_fullname = *smb_fname_rel;
|
||||||
|
|
||||||
|
DBG_DEBUG("rel->base_name changed from %s to %s\n",
|
||||||
|
orig_rel_base_name,
|
||||||
|
smb_fname_rel->base_name);
|
||||||
|
|
||||||
|
new_fullname.base_name = full_path_from_dirfsp_at_basename(
|
||||||
|
talloc_tos(), dirfsp, new_fullname.base_name);
|
||||||
|
if (new_fullname.base_name == NULL) {
|
||||||
|
fd_close(fsp);
|
||||||
|
file_free(NULL, fsp);
|
||||||
|
return NT_STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = fsp_set_smb_fname(fsp, &new_fullname);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
fd_close(fsp);
|
||||||
|
file_free(NULL, fsp);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fsp_set_fd(fsp, fd);
|
||||||
|
|
||||||
|
if (fd >= 0) {
|
||||||
|
ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
|
||||||
|
} else {
|
||||||
|
ret = SMB_VFS_FSTATAT(fsp->conn,
|
||||||
|
dirfsp,
|
||||||
|
smb_fname_rel,
|
||||||
|
&fsp->fsp_name->st,
|
||||||
|
AT_SYMLINK_NOFOLLOW);
|
||||||
|
}
|
||||||
|
if (ret == -1) {
|
||||||
|
status = map_nt_error_from_unix(errno);
|
||||||
|
DBG_DEBUG("SMB_VFS_%sSTAT(%s/%s) failed: %s\n",
|
||||||
|
(fd >= 0) ? "F" : "",
|
||||||
|
dirfsp->fsp_name->base_name,
|
||||||
|
smb_fname_rel->base_name,
|
||||||
|
nt_errstr(status));
|
||||||
|
fd_close(fsp);
|
||||||
|
file_free(NULL, fsp);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsp->fsp_flags.is_directory = S_ISDIR(fsp->fsp_name->st.st_ex_mode);
|
||||||
|
fsp->file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
|
||||||
|
|
||||||
|
smb_fname_rel->st = fsp->fsp_name->st;
|
||||||
|
|
||||||
|
status = fsp_smb_fname_link(fsp,
|
||||||
|
&smb_fname_rel->fsp_link,
|
||||||
|
&smb_fname_rel->fsp);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
DBG_DEBUG("fsp_smb_fname_link() failed: %s\n",
|
||||||
|
nt_errstr(status));
|
||||||
|
fd_close(fsp);
|
||||||
|
file_free(NULL, fsp);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_DEBUG("fsp [%s]: OK, fd=%d\n", fsp_str_dbg(fsp), fd);
|
||||||
|
|
||||||
|
talloc_set_destructor(smb_fname_rel, smb_fname_fsp_destructor);
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void smb_fname_fsp_unlink(struct smb_filename *smb_fname)
|
void smb_fname_fsp_unlink(struct smb_filename *smb_fname)
|
||||||
{
|
{
|
||||||
talloc_set_destructor(smb_fname, NULL);
|
talloc_set_destructor(smb_fname, NULL);
|
||||||
|
@ -5961,6 +5961,31 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(create_options & FILE_OPEN_REPARSE_POINT) &&
|
||||||
|
(smb_fname->fsp != NULL) && /* new files don't have an fsp */
|
||||||
|
VALID_STAT(smb_fname->fsp->fsp_name->st))
|
||||||
|
{
|
||||||
|
mode_t type = (smb_fname->fsp->fsp_name->st.st_ex_mode &
|
||||||
|
S_IFMT);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case S_IFREG:
|
||||||
|
FALL_THROUGH;
|
||||||
|
case S_IFDIR:
|
||||||
|
break;
|
||||||
|
case S_IFLNK:
|
||||||
|
/*
|
||||||
|
* We should never get this far with a symlink
|
||||||
|
* "as such". Report as not existing.
|
||||||
|
*/
|
||||||
|
status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||||
|
goto fail;
|
||||||
|
default:
|
||||||
|
status = NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (req == NULL) {
|
if (req == NULL) {
|
||||||
oplock_request |= INTERNAL_OPEN_ONLY;
|
oplock_request |= INTERNAL_OPEN_ONLY;
|
||||||
}
|
}
|
||||||
|
@ -448,6 +448,9 @@ NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
|
|||||||
bool posix,
|
bool posix,
|
||||||
struct smb_filename **_smb_fname,
|
struct smb_filename **_smb_fname,
|
||||||
struct open_symlink_err **_symlink_err);
|
struct open_symlink_err **_symlink_err);
|
||||||
|
NTSTATUS openat_pathref_fsp_lcomp(struct files_struct *dirfsp,
|
||||||
|
struct smb_filename *smb_fname_rel,
|
||||||
|
uint32_t ucf_flags);
|
||||||
NTSTATUS readlink_talloc(
|
NTSTATUS readlink_talloc(
|
||||||
TALLOC_CTX *mem_ctx,
|
TALLOC_CTX *mem_ctx,
|
||||||
struct files_struct *dirfsp,
|
struct files_struct *dirfsp,
|
||||||
|
@ -1136,6 +1136,21 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
|
|||||||
&state->info,
|
&state->info,
|
||||||
&in_context_blobs,
|
&in_context_blobs,
|
||||||
state->out_context_blobs);
|
state->out_context_blobs);
|
||||||
|
if (NT_STATUS_IS_OK(status) &&
|
||||||
|
!(state->in_create_options & FILE_OPEN_REPARSE_POINT))
|
||||||
|
{
|
||||||
|
|
||||||
|
mode_t mode = state->result->fsp_name->st.st_ex_mode;
|
||||||
|
|
||||||
|
if (!(S_ISREG(mode) || S_ISDIR(mode))) {
|
||||||
|
/*
|
||||||
|
* Only open files and dirs without
|
||||||
|
* FILE_OPEN_REPARSE_POINT
|
||||||
|
*/
|
||||||
|
close_file_free(smb1req, &state->result, ERROR_CLOSE);
|
||||||
|
status = NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!NT_STATUS_IS_OK(status)) {
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
if (open_was_deferred(smb1req->xconn, smb1req->mid)) {
|
if (open_was_deferred(smb1req->xconn, smb1req->mid)) {
|
||||||
SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(smb2req->profile);
|
SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(smb2req->profile);
|
||||||
|
Loading…
Reference in New Issue
Block a user