From: Ralph Boehme Date: Fri, 18 Oct 2024 06:02:21 +0000 (+0200) Subject: smbd: check for handle lease break on destination when renaming X-Git-Tag: tdb-1.4.13~720 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e21effb2dd27c8ea4700abd5e8884e312988f61e;p=thirdparty%2Fsamba.git smbd: check for handle lease break on destination when renaming BUG: https://bugzilla.samba.org/show_bug.cgi?id=15608 Signed-off-by: Ralph Boehme Reviewed-by: Stefan Metzmacher --- diff --git a/selftest/knownfail.d/lease_rename_with_overwrite b/selftest/knownfail.d/lease_rename_with_overwrite deleted file mode 100644 index 1c976b64663..00000000000 --- a/selftest/knownfail.d/lease_rename_with_overwrite +++ /dev/null @@ -1 +0,0 @@ -^samba3.smb2.lease.v2_rename_target_overwrite\(fileserver\) diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c index 10aff8b5cc4..4038d1dd8de 100644 --- a/source3/smbd/smb2_setinfo.c +++ b/source3/smbd/smb2_setinfo.c @@ -30,6 +30,7 @@ #include "source3/lib/dbwrap/dbwrap_watch.h" #include "messages.h" #include "librpc/gen_ndr/ndr_quota.h" +#include "libcli/security/security.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_SMB2 @@ -173,9 +174,11 @@ struct smbd_smb2_setinfo_state { struct smbd_smb2_request *smb2req; struct files_struct *fsp; struct share_mode_lock *lck; + struct files_struct *dstfsp; uint16_t file_info_level; DATA_BLOB data; bool delay; + bool rename_dst_check_done; }; static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req); @@ -389,6 +392,8 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx, static void smbd_smb2_setinfo_lease_break_fsp_check(struct tevent_req *req); static void smbd_smb2_setinfo_lease_break_fsp_done(struct tevent_req *subreq); +static void smbd_smb2_setinfo_rename_dst_check(struct tevent_req *req); +static void smbd_smb2_setinfo_rename_dst_delay_done(struct tevent_req *subreq); static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req) { @@ -399,6 +404,15 @@ static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req) state->delay = false; + smbd_smb2_setinfo_rename_dst_check(req); + if (!tevent_req_is_in_progress(req)) { + return; + } + if (state->delay) { + DBG_DEBUG("Waiting for h-lease breaks on rename destination\n"); + return; + } + smbd_smb2_setinfo_lease_break_fsp_check(req); if (!tevent_req_is_in_progress(req)) { return; @@ -539,6 +553,216 @@ static void smbd_smb2_setinfo_lease_break_fsp_done(struct tevent_req *subreq) tevent_req_done(req); } +static void smbd_smb2_setinfo_rename_dst_check(struct tevent_req *req) +{ + struct smbd_smb2_setinfo_state *state = tevent_req_data( + req, struct smbd_smb2_setinfo_state); + struct tevent_req *subreq = NULL; + struct timeval timeout; + bool overwrite = false; + struct files_struct *fsp = state->fsp; + struct files_struct *dst_dirfsp = NULL; + struct smb_filename *smb_fname_dst = NULL; + char *dst_original_lcomp = NULL; + bool has_other_open; + NTSTATUS status; + NTSTATUS close_status; + + if (state->file_info_level != SMB2_FILE_RENAME_INFORMATION_INTERNAL) { + return; + } + + if (state->rename_dst_check_done) { + return; + } + + status = smb2_parse_file_rename_information(state, + fsp->conn, + state->smb2req->smb1req, + (char *)state->data.data, + state->data.length, + fsp, + fsp->fsp_name, + &overwrite, + &dst_dirfsp, + &smb_fname_dst, + &dst_original_lcomp); + if (tevent_req_nterror(req, status)) { + return; + } + TALLOC_FREE(dst_original_lcomp); + + if (!VALID_STAT(smb_fname_dst->st)) { + /* Doesn't exist, nothing to do here */ + return; + } + if (strequal(fsp->fsp_name->base_name, smb_fname_dst->base_name) && + strequal(fsp->fsp_name->stream_name, smb_fname_dst->stream_name)) + { + return; + } + if (!overwrite) { + /* Return the correct error */ + tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_COLLISION); + return; + } + + status = SMB_VFS_CREATE_FILE( + fsp->conn, + NULL, + dst_dirfsp, + smb_fname_dst, + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ + | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, + FILE_OPEN, /* create_disposition*/ + 0, /* create_options */ + FILE_ATTRIBUTE_NORMAL, + INTERNAL_OPEN_ONLY, /* oplock_request */ + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &state->dstfsp, /* result */ + NULL, /* psbuf */ + NULL, + NULL); /* create context */ + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + /* A race, file was deleted */ + return; + } + tevent_req_nterror(req, status); + return; + } + + state->lck = get_existing_share_mode_lock(state, state->dstfsp->file_id); + if (state->lck == NULL) { + close_status = close_file_free(NULL, &state->dstfsp, ERROR_CLOSE); + if (!NT_STATUS_IS_OK(close_status)) { + DBG_ERR("close_file_free failed\n"); + } + return; + } + + timeout = tevent_timeval_set(OPLOCK_BREAK_TIMEOUT, 0); + timeout = timeval_sum(&state->smb2req->request_time, &timeout); + + subreq = delay_for_handle_lease_break_send(state, + state->ev, + timeout, + state->dstfsp, + false, + &state->lck); + if (subreq == NULL) { + close_status = close_file_free(NULL, &state->dstfsp, ERROR_CLOSE); + if (!NT_STATUS_IS_OK(close_status)) { + DBG_ERR("close_file_free failed\n"); + } + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + if (tevent_req_is_in_progress(subreq)) { + state->delay = true; + tevent_req_set_callback(subreq, + smbd_smb2_setinfo_rename_dst_delay_done, + req); + return; + } + + status = delay_for_handle_lease_break_recv(subreq, state, &state->lck); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + close_status = close_file_free(NULL, &state->dstfsp, ERROR_CLOSE); + if (!NT_STATUS_IS_OK(close_status)) { + DBG_ERR("close_file_free failed\n"); + } + tevent_req_nterror(req, status); + return; + } + + has_other_open = has_other_nonposix_opens(state->lck, state->dstfsp); + TALLOC_FREE(state->lck); + + status = close_file_free(NULL, &state->dstfsp, NORMAL_CLOSE); + if (tevent_req_nterror(req, status)) { + DBG_ERR("close_file_free failed\n"); + return; + } + + if (has_other_open) { + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + state->rename_dst_check_done = true; + return; +} + +static void smbd_smb2_setinfo_rename_dst_delay_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbd_smb2_setinfo_state *state = tevent_req_data( + req, struct smbd_smb2_setinfo_state); + struct smbXsrv_session *session = state->smb2req->session; + bool has_other_open; + NTSTATUS close_status; + NTSTATUS status; + bool ok; + + status = delay_for_handle_lease_break_recv(subreq, state, &state->lck); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + close_status = close_file_free(NULL, + &state->dstfsp, + NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(close_status)) { + DBG_ERR("close_file_free failed\n"); + } + tevent_req_nterror(req, status); + return; + } + + /* + * Make sure we run as the user again + */ + ok = change_to_user_and_service(state->dstfsp->conn, + session->global->session_wire_id); + if (!ok) { + close_status = close_file_free(NULL, + &state->dstfsp, + NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(close_status)) { + DBG_ERR("close_file_free failed\n"); + } + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + + has_other_open = has_other_nonposix_opens(state->lck, state->dstfsp); + TALLOC_FREE(state->lck); + + status = close_file_free(NULL, &state->dstfsp, NORMAL_CLOSE); + if (tevent_req_nterror(req, status)) { + DBG_ERR("close_file_free failed\n"); + return; + } + + if (has_other_open) { + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + + /* + * We've finished breaking H-lease on the rename destination, now + * trigger the fsp check. + */ + state->rename_dst_check_done = true; + smbd_smb2_setinfo_lease_break_check(req); +} + static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req) { NTSTATUS status;