1
0
mirror of https://github.com/samba-team/samba.git synced 2025-10-07 03:33:18 +03:00

smbd: Return NT_STATUS_STOPPED_ON_SYMLINK

Do this for "follow symlinks = now" and smb2 unix extensions

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
This commit is contained in:
Volker Lendecke
2024-11-20 12:56:33 +01:00
committed by Ralph Boehme
parent cc0ed15fec
commit 34be8ef596
5 changed files with 161 additions and 44 deletions

View File

@@ -1,4 +0,0 @@
^samba.tests.smb2symlink.samba.tests.smb2symlink.Smb2SymlinkTests.test_symlinkerror_directory
^samba.tests.smb2symlink.samba.tests.smb2symlink.Smb2SymlinkTests.test_symlinkerror_file
^samba.tests.smb2symlink.samba.tests.smb2symlink.Smb2SymlinkTests.test_symlinkerror_absolute_outside_share
^samba.tests.smb2symlink.samba.tests.smb2symlink.Smb2SymlinkTests.test_symlinkerror_absolute_inshare

View File

@@ -1343,11 +1343,17 @@ EOF
return 1
fi
echo "$out" | grep 'NT_STATUS_OBJECT_NAME_NOT_FOUND'
if [ "$PROTOCOL" = "SMB3" ]; then
expected_error="NT_STATUS_STOPPED_ON_SYMLINK"
else
expected_error="NT_STATUS_OBJECT_NAME_NOT_FOUND"
fi
echo "$out" | grep "$expected_error"
ret=$?
if [ $ret -ne 0 ]; then
echo "$out"
echo "failed - should get NT_STATUS_OBJECT_NAME_NOT_FOUND getting \\nosymlinks\\source"
echo "failed - should get ${expected_error} getting \\nosymlinks\\source"
return 1
fi

View File

@@ -41,11 +41,7 @@ uint32_t ucf_flags_from_smb_request(struct smb_request *req)
}
if (req->posix_pathnames) {
ucf_flags |= UCF_POSIX_PATHNAMES;
if (!conn_using_smb2(req->sconn)) {
ucf_flags |= UCF_LCOMP_LNK_OK;
}
ucf_flags |= (UCF_POSIX_PATHNAMES|UCF_LCOMP_LNK_OK);
}
if (req->flags2 & FLAGS2_DFS_PATHNAMES) {
ucf_flags |= UCF_DFS_PATHNAME;

View File

@@ -1216,17 +1216,6 @@ static NTSTATUS open_file(
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
if (S_ISLNK(smb_fname->st.st_ex_mode) &&
!posix_open)
{
/*
* Don't allow stat opens on symlinks directly unless
* it's a POSIX open. Match the return code from
* openat_pathref_fsp().
*/
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
if (!fsp->fsp_flags.is_pathref) {
/*
* There is only one legit case where end up here:
@@ -1236,11 +1225,6 @@ static NTSTATUS open_file(
* pathref fsp at this point. The subsequent checks
* assert this.
*/
if (!(smb_fname->flags & SMB_FILENAME_POSIX_PATH)) {
DBG_ERR("[%s] is not a POSIX pathname\n",
smb_fname_str_dbg(smb_fname));
return NT_STATUS_INTERNAL_ERROR;
}
if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
DBG_ERR("[%s] is not a symlink\n",
smb_fname_str_dbg(smb_fname));
@@ -1290,13 +1274,19 @@ static NTSTATUS open_file(
fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
fsp->file_pid = req ? req->smbpid : 0;
fsp->fsp_flags.can_lock = true;
fsp->fsp_flags.can_read = ((access_mask & FILE_READ_DATA) != 0);
fsp->fsp_flags.can_write =
CAN_WRITE(conn) &&
((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0);
if (fsp->fsp_name->twrp != 0) {
if (file_existed && S_ISLNK(smb_fname->st.st_ex_mode)) {
fsp->fsp_flags.can_lock = false;
fsp->fsp_flags.can_read = false;
fsp->fsp_flags.can_write = false;
} else {
fsp->fsp_flags.can_lock = true;
fsp->fsp_flags.can_read = ((access_mask & FILE_READ_DATA) !=
0);
fsp->fsp_flags.can_write = CAN_WRITE(conn) &&
((access_mask &
(FILE_WRITE_DATA |
FILE_APPEND_DATA)) != 0) &&
(fsp->fsp_name->twrp == 0);
}
fsp->print_file = NULL;
fsp->fsp_flags.modified = false;

View File

@@ -479,7 +479,9 @@ static void smbd_smb2_request_create_done(struct tevent_req *tsubreq)
if (smbd_smb2_is_compound(smb2req)) {
smb2req->compound_create_err = status;
}
error = smbd_smb2_create_error(smb2req, status, NULL);
error = smbd_smb2_create_error(smb2req,
status,
symlink_reparse);
if (!NT_STATUS_IS_OK(error)) {
smbd_server_connection_terminate(smb2req->xconn,
nt_errstr(error));
@@ -732,6 +734,9 @@ struct smbd_smb2_create_state {
uint64_t out_file_id_persistent;
uint64_t out_file_id_volatile;
struct smb2_create_blobs *out_context_blobs;
/* symlink error data */
struct reparse_data_buffer *symlink_err;
};
static void smbd_smb2_create_purge_replay_cache(struct tevent_req *req,
@@ -1187,14 +1192,42 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
state->in_create_disposition,
state->in_create_options);
status = filename_convert_dirfsp(
req,
smb1req->conn,
state->fname,
ucf_flags,
state->twrp_time,
&dirfsp,
&smb_fname);
if (lp_follow_symlinks(SNUM(smb1req->conn)) &&
(state->posx == NULL)) {
status = filename_convert_dirfsp(mem_ctx,
smb1req->conn,
state->fname,
ucf_flags,
state->twrp_time,
&dirfsp,
&smb_fname);
} else {
struct smb_filename *smb_fname_rel = NULL;
status = filename_convert_dirfsp_nosymlink(
mem_ctx,
smb1req->conn,
smb1req->conn->cwd_fsp,
state->fname,
ucf_flags,
state->twrp_time,
&dirfsp,
&smb_fname,
&smb_fname_rel,
&state->symlink_err);
TALLOC_FREE(smb_fname_rel);
if ((state->symlink_err != NULL) &&
!(state->in_create_options & FILE_OPEN_REPARSE_POINT))
{
if (dirfsp != NULL) {
close_file_free(NULL, &dirfsp, ERROR_CLOSE);
}
TALLOC_FREE(smb_fname);
TALLOC_FREE(smb_fname_rel);
status = NT_STATUS_STOPPED_ON_SYMLINK;
}
}
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, state->ev);
}
@@ -1636,6 +1669,56 @@ static void smbd_smb2_create_after_exec(struct tevent_req *req)
state->out_file_attributes = fdos_mode(state->result);
if ((state->out_file_attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
(!(state->in_create_options & FILE_OPEN_REPARSE_POINT)))
{
uint32_t tag;
uint8_t *data = NULL;
uint32_t len = 0;
status = fsctl_get_reparse_point(
state->result, talloc_tos(), &tag, &data, 65536, &len);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
if (tag != IO_REPARSE_TAG_SYMLINK) {
status = NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
goto fail;
}
state->symlink_err = talloc_zero(state,
struct reparse_data_buffer);
if (state->symlink_err == NULL) {
TALLOC_FREE(data);
status = NT_STATUS_NO_MEMORY;
goto fail;
}
status = reparse_data_buffer_parse(state->symlink_err,
state->symlink_err,
data,
len);
TALLOC_FREE(data);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("reparse_data_buffer_parse failed: %s\n",
nt_errstr(status));
goto fail;
}
/*
* Checked above, just to make sure
*/
SMB_ASSERT(state->symlink_err->tag == IO_REPARSE_TAG_SYMLINK);
DBG_DEBUG("Redirecting to %s\n",
state->symlink_err->parsed.lnk.substitute_name);
status = NT_STATUS_STOPPED_ON_SYMLINK;
goto fail;
}
if (state->mxac != NULL) {
NTTIME last_write_time;
@@ -1919,11 +2002,57 @@ static NTSTATUS smbd_smb2_create_recv(struct tevent_req *req,
struct smb2_create_blobs *out_context_blobs,
struct reparse_data_buffer **symlink_reparse)
{
NTSTATUS status;
NTSTATUS status = NT_STATUS_OK;
struct smbd_smb2_create_state *state = tevent_req_data(req,
struct smbd_smb2_create_state);
bool error;
if (tevent_req_is_nterror(req, &status)) {
error = tevent_req_is_nterror(req, &status);
if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
struct symlink_reparse_struct *lnk = &state->symlink_err
->parsed.lnk;
size_t fname_len = strlen(state->fname);
/*
* filename_convert_dirfsp_nosymlink() calculates
* unparsed_path_length. Just assert it did not mess up big
* time.
*/
SMB_ASSERT(lnk->unparsed_path_length <= fname_len);
if (lnk->unparsed_path_length != 0) {
bool ok;
char *unparsed_unix = NULL;
char *utf_16 = NULL;
size_t utf_16_len;
unparsed_unix = state->fname + fname_len -
lnk->unparsed_path_length;
ok = convert_string_talloc(talloc_tos(),
CH_UNIX,
CH_UTF16,
unparsed_unix,
lnk->unparsed_path_length,
&utf_16,
&utf_16_len);
if (!ok) {
tevent_req_received(req);
return NT_STATUS_INTERNAL_ERROR;
}
TALLOC_FREE(utf_16);
if (utf_16_len > UINT16_MAX) {
tevent_req_received(req);
return NT_STATUS_BUFFER_OVERFLOW;
}
lnk->unparsed_path_length = utf_16_len;
}
*symlink_reparse = talloc_move(mem_ctx, &state->symlink_err);
}
if (error) {
tevent_req_received(req);
return status;
}